Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configurations and support for auto formatting tools #4654

Merged
merged 12 commits into from
Oct 22, 2023
Merged
12 changes: 12 additions & 0 deletions .github/workflows/formatting_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: formatting_check
run-name: Check code formatting
on: [push, pull_request]
jobs:
formatting_check:
runs-on: ubuntu-latest
steps:
- name: Install dependencies
- uses: actions/checkout@v3
- uses: paolorechia/[email protected]
with:
tox_env: "format_check"
14 changes: 14 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks

repos:
- repo: https://github.com/psf/black
rev: 22.12.0
hooks:
- id: black

- repo: https://github.com/pycqa/isort
rev: 5.11.4
hooks:
- id: isort
name: isort (python)
22 changes: 13 additions & 9 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,21 @@ request and your code will ship in no time.

1. Fork the beets repository and clone it (see above) to create a
workspace.
2. Make your changes.
3. Add tests. If you’ve fixed a bug, write a test to ensure that you’ve
2. Install pre-commit, following the instructions `here
<https://pre-commit.com/>`_.
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.
4. Add documentation. If you’ve added a new command flag, for example,
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.
5. Add a changelog entry to ``docs/changelog.rst`` near the top of the
6. Add a changelog entry to ``docs/changelog.rst`` near the top of the
document.
6. Run the tests and style checker. The easiest way to run the tests is
7. Run the tests and style checker. The easiest way to run the tests is
to use `tox`_. For more information on running tests, see :ref:`testing`.
7. Push to your fork and open a pull request! We’ll be in touch shortly.
8. If you add commits to a pull request, please add a comment or
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.

Expand Down Expand Up @@ -214,11 +216,13 @@ There are a few coding conventions we use in beets:
Style
-----

We follow `PEP 8`_ and `google's docstring format`_.
We follow `black`_ formatting and `google's docstring format`_.

You can use ``tox -e lint`` to check your code for any style errors.
Running ``tox -e format`` will automatically format your code according
to the specifications required by the project.

.. _PEP 8: https://www.python.org/dev/peps/pep-0008/
.. _black: https://black.readthedocs.io/en/stable/
.. _google's docstring format: https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings

Handling Paths
Expand Down
15 changes: 8 additions & 7 deletions beets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,29 @@
# included in all copies or substantial portions of the Software.


import confuse
from sys import stderr

__version__ = '1.6.1'
__author__ = 'Adrian Sampson <[email protected]>'
import confuse

__version__ = "1.6.1"
__author__ = "Adrian Sampson <[email protected]>"


class IncludeLazyConfig(confuse.LazyConfig):
"""A version of Confuse's LazyConfig that also merges in data from
YAML files specified in an `include` setting.
"""

def read(self, user=True, defaults=True):
super().read(user, defaults)

try:
for view in self['include']:
for view in self["include"]:
self.set_file(view.as_filename())
except confuse.NotFoundError:
pass
except confuse.ConfigReadError as err:
stderr.write("configuration `import` failed: {}"
.format(err.reason))
stderr.write("configuration `import` failed: {}".format(err.reason))


config = IncludeLazyConfig('beets', __name__)
config = IncludeLazyConfig("beets", __name__)
1 change: 1 addition & 0 deletions beets/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@


import sys

from .ui import main

if __name__ == "__main__":
Expand Down
132 changes: 84 additions & 48 deletions beets/art.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@
"""


from tempfile import NamedTemporaryFile
import os
from tempfile import NamedTemporaryFile

from beets.util import displayable_path, syspath, bytestring_path
from beets.util.artresizer import ArtResizer
import mediafile

from beets.util import bytestring_path, displayable_path, syspath
from beets.util.artresizer import ArtResizer


def mediafile_image(image_path, maxwidth=None):
"""Return a `mediafile.Image` object for the path.
"""
"""Return a `mediafile.Image` object for the path."""

with open(syspath(image_path), 'rb') as f:
with open(syspath(image_path), "rb") as f:
data = f.read()
return mediafile.Image(data, type=mediafile.ImageType.front)

Expand All @@ -39,31 +39,43 @@ def get_art(log, item):
try:
mf = mediafile.MediaFile(syspath(item.path))
except mediafile.UnreadableFileError as exc:
log.warning('Could not extract art from {0}: {1}',
displayable_path(item.path), exc)
log.warning(
"Could not extract art from {0}: {1}",
displayable_path(item.path),
exc,
)
return

return mf.art


def embed_item(log, item, imagepath, maxwidth=None, itempath=None,
compare_threshold=0, ifempty=False, as_album=False, id3v23=None,
quality=0):
"""Embed an image into the item's media file.
"""
def embed_item(
log,
item,
imagepath,
maxwidth=None,
itempath=None,
compare_threshold=0,
ifempty=False,
as_album=False,
id3v23=None,
quality=0,
):
"""Embed an image into the item's media file."""
# Conditions.
if compare_threshold:
is_similar = check_art_similarity(
log, item, imagepath, compare_threshold)
log, item, imagepath, compare_threshold
)
if is_similar is None:
log.warning('Error while checking art similarity; skipping.')
log.warning("Error while checking art similarity; skipping.")
return
elif not is_similar:
log.info('Image not similar; skipping.')
log.info("Image not similar; skipping.")
return

if ifempty and get_art(log, item):
log.info('media file already contained art')
log.info("media file already contained art")
return

# Filters.
Expand All @@ -72,52 +84,74 @@ def embed_item(log, item, imagepath, maxwidth=None, itempath=None,

# Get the `Image` object from the file.
try:
log.debug('embedding {0}', displayable_path(imagepath))
log.debug("embedding {0}", displayable_path(imagepath))
image = mediafile_image(imagepath, maxwidth)
except OSError as exc:
log.warning('could not read image file: {0}', exc)
log.warning("could not read image file: {0}", exc)
return

# Make sure the image kind is safe (some formats only support PNG
# and JPEG).
if image.mime_type not in ('image/jpeg', 'image/png'):
log.info('not embedding image of unsupported type: {}',
image.mime_type)
if image.mime_type not in ("image/jpeg", "image/png"):
log.info("not embedding image of unsupported type: {}", image.mime_type)
return

item.try_write(path=itempath, tags={'images': [image]}, id3v23=id3v23)
item.try_write(path=itempath, tags={"images": [image]}, id3v23=id3v23)


def embed_album(log, album, maxwidth=None, quiet=False, compare_threshold=0,
ifempty=False, quality=0):
"""Embed album art into all of the album's items.
"""
def embed_album(
log,
album,
maxwidth=None,
quiet=False,
compare_threshold=0,
ifempty=False,
quality=0,
):
"""Embed album art into all of the album's items."""
imagepath = album.artpath
if not imagepath:
log.info('No album art present for {0}', album)
log.info("No album art present for {0}", album)
return
if not os.path.isfile(syspath(imagepath)):
log.info('Album art not found at {0} for {1}',
displayable_path(imagepath), album)
log.info(
"Album art not found at {0} for {1}",
displayable_path(imagepath),
album,
)
return
if maxwidth:
imagepath = resize_image(log, imagepath, maxwidth, quality)

log.info('Embedding album art into {0}', album)
log.info("Embedding album art into {0}", album)

for item in album.items():
embed_item(log, item, imagepath, maxwidth, None, compare_threshold,
ifempty, as_album=True, quality=quality)
embed_item(
log,
item,
imagepath,
maxwidth,
None,
compare_threshold,
ifempty,
as_album=True,
quality=quality,
)


def resize_image(log, imagepath, maxwidth, quality):
"""Returns path to an image resized to maxwidth and encoded with the
specified quality level.
"""
log.debug('Resizing album art to {0} pixels wide and encoding at quality \
level {1}', maxwidth, quality)
imagepath = ArtResizer.shared.resize(maxwidth, syspath(imagepath),
quality=quality)
log.debug(
"Resizing album art to {0} pixels wide and encoding at quality \
level {1}",
maxwidth,
quality,
)
imagepath = ArtResizer.shared.resize(
maxwidth, syspath(imagepath), quality=quality
)
return imagepath


Expand Down Expand Up @@ -151,20 +185,22 @@ def extract(log, outpath, item):
art = get_art(log, item)
outpath = bytestring_path(outpath)
if not art:
log.info('No album art present in {0}, skipping.', item)
log.info("No album art present in {0}, skipping.", item)
return

# Add an extension to the filename.
ext = mediafile.image_extension(art)
if not ext:
log.warning('Unknown image type in {0}.',
displayable_path(item.path))
log.warning("Unknown image type in {0}.", displayable_path(item.path))
return
outpath += bytestring_path('.' + ext)

log.info('Extracting album art from: {0} to: {1}',
item, displayable_path(outpath))
with open(syspath(outpath), 'wb') as f:
outpath += bytestring_path("." + ext)

log.info(
"Extracting album art from: {0} to: {1}",
item,
displayable_path(outpath),
)
with open(syspath(outpath), "wb") as f:
f.write(art)
return outpath

Expand All @@ -178,7 +214,7 @@ def extract_first(log, outpath, items):

def clear(log, lib, query):
items = lib.items(query)
log.info('Clearing album art from {0} items', len(items))
log.info("Clearing album art from {0} items", len(items))
for item in items:
log.debug('Clearing art for {0}', item)
item.try_write(tags={'images': None})
log.debug("Clearing art for {0}", item)
item.try_write(tags={"images": None})
Loading
Loading