diff --git a/.github/workflows/formatting_check.yml b/.github/workflows/formatting_check.yml index bd7f02b86..76154fe4d 100644 --- a/.github/workflows/formatting_check.yml +++ b/.github/workflows/formatting_check.yml @@ -6,7 +6,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Install dependencies - - uses: actions/checkout@v3 - - uses: paolorechia/pox@v1.0.1 + uses: actions/checkout@v3 + + - name: Run formatting check + uses: paolorechia/pox@v1.0.1 with: tox_env: "format_check" diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d91966011..f5edb70f9 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -42,12 +42,12 @@ Non-Programming 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 `our - forums `__ under the “Show and Tell” - category for a chance to get featured in `the + 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 in `our forums `__ - by responding to support requests or driving some new discussions. +- Consider helping out fellow users by by `responding to support requests + `__ . Programming ----------- @@ -119,7 +119,8 @@ 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 -`forums `__ if you have any questions. +`discussion board `__ +if you have any questions. We maintain a list of issues we reserved for those new to open source labeled `“first timers diff --git a/README.rst b/README.rst index 42f770278..0813bd6cc 100644 --- a/README.rst +++ b/README.rst @@ -115,12 +115,11 @@ Contact 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`_ or `Discourse`_! + `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 -.. _Discourse: https://discourse.beets.io/ Authors ------- diff --git a/README_kr.rst b/README_kr.rst index a6a95ec5a..c12fc8b71 100644 --- a/README_kr.rst +++ b/README_kr.rst @@ -104,5 +104,5 @@ Read More `Adrian Sampson`_ 와 많은 사람들의 지지를 받아 Beets를 만들었다. 돕고 싶다면 `forum`_.를 방문하면 된다. -.. _forum: https://discourse.beets.io +.. _forum: https://github.com/beetbox/beets/discussions/ .. _Adrian Sampson: https://www.cs.cornell.edu/~asampson/ diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 03f85ac77..9806d5226 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -166,8 +166,8 @@ class FieldQuery(Query, Generic[P]): def __repr__(self) -> str: return ( - "{0.__class__.__name__}({0.field!r}, {0.pattern!r}, " - "{0.fast})".format(self) + f"{self.__class__.__name__}({self.field!r}, {self.pattern!r}, " + f"fast={self.fast})" ) def __eq__(self, other) -> bool: @@ -205,7 +205,7 @@ class NoneQuery(FieldQuery[None]): return obj.get(self.field) is None def __repr__(self) -> str: - return "{0.__class__.__name__}({0.field!r}, {0.fast})".format(self) + return f"{self.__class__.__name__}({self.field!r}, {self.fast})" class StringFieldQuery(FieldQuery[P]): @@ -471,7 +471,7 @@ class CollectionQuery(Query): return clause, subvals def __repr__(self) -> str: - return "{0.__class__.__name__}({0.subqueries!r})".format(self) + return f"{self.__class__.__name__}({self.subqueries!r})" def __eq__(self, other) -> bool: return super().__eq__(other) and self.subqueries == other.subqueries @@ -511,8 +511,8 @@ class AnyFieldQuery(CollectionQuery): def __repr__(self) -> str: return ( - "{0.__class__.__name__}({0.pattern!r}, {0.fields!r}, " - "{0.query_class.__name__})".format(self) + f"{self.__class__.__name__}({self.pattern!r}, {self.fields!r}, " + f"{self.query_class.__name__})" ) def __eq__(self, other) -> bool: @@ -577,7 +577,7 @@ class NotQuery(Query): return not self.subquery.match(obj) def __repr__(self) -> str: - return "{0.__class__.__name__}({0.subquery!r})".format(self) + return f"{self.__class__.__name__}({self.subquery!r})" def __eq__(self, other) -> bool: return super().__eq__(other) and self.subquery == other.subquery @@ -883,6 +883,9 @@ class Sort: def __eq__(self, other) -> bool: return type(self) is type(other) + def __repr__(self): + return f"{self.__class__.__name__}()" + class MultipleSort(Sort): """Sort that encapsulates multiple sub-sorts.""" @@ -934,7 +937,7 @@ class MultipleSort(Sort): return items def __repr__(self): - return f"MultipleSort({self.sorts!r})" + return f"{self.__class__.__name__}({self.sorts!r})" def __hash__(self): return hash(tuple(self.sorts)) @@ -972,10 +975,9 @@ class FieldSort(Sort): return sorted(objs, key=key, reverse=not self.ascending) def __repr__(self) -> str: - return "<{}: {}{}>".format( - type(self).__name__, - self.field, - "+" if self.ascending else "-", + return ( + f"{self.__class__.__name__}" + f"({self.field!r}, ascending={self.ascending!r})" ) def __hash__(self) -> int: diff --git a/beets/importer.py b/beets/importer.py index f7c6232aa..55ee29226 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -412,7 +412,7 @@ class ImportSession: def ask_resume(self, toppath): """If import of `toppath` was aborted in an earlier session, ask - user if she wants to resume the import. + user if they want to resume the import. Determines the return value of `is_resuming(toppath)`. """ diff --git a/beets/library.py b/beets/library.py index d0019fb80..7507f5d34 100644 --- a/beets/library.py +++ b/beets/library.py @@ -151,6 +151,12 @@ class PathQuery(dbcore.FieldQuery): dir_blob, ) + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}({self.field!r}, {self.pattern!r}, " + f"fast={self.fast}, case_sensitive={self.case_sensitive})" + ) + # Library-specific field types. @@ -1518,9 +1524,12 @@ def parse_query_parts(parts, model_cls): case_insensitive = beets.config["sort_case_insensitive"].get(bool) - return dbcore.parse_sorted_query( + query, sort = dbcore.parse_sorted_query( model_cls, parts, prefixes, case_insensitive ) + log.debug("Parsed query: {!r}", query) + log.debug("Parsed sort: {!r}", sort) + return query, sort def parse_query_string(s, model_cls): diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 5bdd27705..3385e4221 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -425,6 +425,8 @@ class DiscogsPlugin(BeetsPlugin): catalogno = result.data["labels"][0].get("catno") labelid = result.data["labels"][0].get("id") + cover_art_url = self.select_cover_art(result) + # Additional cleanups (various artists name, catalog number, media). if va: artist = config["va_name"].as_str() @@ -474,8 +476,19 @@ class DiscogsPlugin(BeetsPlugin): discogs_albumid=discogs_albumid, discogs_labelid=labelid, discogs_artistid=artist_id, + cover_art_url=cover_art_url, ) + def select_cover_art(self, result): + """Returns the best candidate image, if any, from a Discogs `Release` object.""" + if result.data.get("images") and len(result.data.get("images")) > 0: + # The first image in this list appears to be the one displayed first + # on the release page - even if it is not flagged as `type: "primary"` - and + # so it is the best candidate for the cover art. + return result.data.get("images")[0].get("uri") + + return None + def format(self, classification): if classification: return ( diff --git a/beetsplug/edit.py b/beetsplug/edit.py index 1e934317d..323dd9e41 100644 --- a/beetsplug/edit.py +++ b/beetsplug/edit.py @@ -220,7 +220,7 @@ class EditPlugin(plugins.BeetsPlugin): - `fields`: The set of field names to edit (or None to edit everything). """ - # Present the YAML to the user and let her change it. + # Present the YAML to the user and let them change it. success = self.edit_objects(objs, fields) # Save the new data. @@ -370,7 +370,7 @@ class EditPlugin(plugins.BeetsPlugin): if not obj._db or obj.id is None: obj.id = -i - # Present the YAML to the user and let her change it. + # Present the YAML to the user and let them change it. fields = self._get_fields(album=False, extra=[]) success = self.edit_objects(task.items, fields) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index f1b012a5f..93d3f2c57 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -1098,7 +1098,7 @@ class CoverArtUrl(RemoteArtSource): image_url = None try: # look for cover_art_url on album or first track - if album.cover_art_url: + if album.get("cover_art_url"): image_url = album.cover_art_url else: image_url = album.items().get().cover_art_url diff --git a/docs/_static/beets.css b/docs/_static/beets.css new file mode 100644 index 000000000..243ae74cc --- /dev/null +++ b/docs/_static/beets.css @@ -0,0 +1,12 @@ +html[data-theme="light"] { + --pst-color-secondary: #a23632; +} +html[data-theme="light"] { + --pst-color-inline-code: #a23632; +} + +/* beetroot red: #a23632 */ +/* beetroot green: #1B5801 */ +/* beetroot green light: rgb(27, 150, 50) */ +/* pydata teal (primary): #126A7E */ +/* pydata violet (secondary): #7D0E70 */ diff --git a/docs/_static/beets_logo.png b/docs/_static/beets_logo.png new file mode 100644 index 000000000..0359260ad Binary files /dev/null and b/docs/_static/beets_logo.png differ diff --git a/docs/changelog.rst b/docs/changelog.rst index 13e75a904..a6ae89a7a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -17,6 +17,8 @@ Major new features: New features: +* :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` diff --git a/docs/conf.py b/docs/conf.py index 4514bbe2b..0220ab48b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -59,5 +59,16 @@ man_pages = [ ), ] -# Options for Alabaster theme -html_theme_options = {"fixed_sidebar": True} +# Options for pydata theme +html_theme = 'pydata_sphinx_theme' +html_theme_options = { + 'collapse_navigation': True, + "logo": { + "text": "beets", + }, + "pygment_light_style": "bw", +} +html_title = "beets" +html_logo = "_static/beets_logo.png" +html_static_path = ['_static'] +html_css_files = ['beets.css'] diff --git a/docs/faq.rst b/docs/faq.rst index 814f87b7a..44c8fa25c 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -6,7 +6,7 @@ Got a question that isn't answered here? Try the `discussion board`_, or :ref:`filing an issue ` in the bug tracker. .. _mailing list: https://groups.google.com/group/beets-users -.. _discussion board: https://discourse.beets.io +.. _discussion board: https://github.com/beetbox/beets/discussions/ .. contents:: :local: diff --git a/docs/guides/tagger.rst b/docs/guides/tagger.rst index d47ee3c4a..68ad908e8 100644 --- a/docs/guides/tagger.rst +++ b/docs/guides/tagger.rst @@ -309,4 +309,4 @@ If we haven't made the process clear, please post on `the discussion board`_ and we'll try to improve this guide. .. _the mailing list: https://groups.google.com/group/beets-users -.. _the discussion board: https://discourse.beets.io +.. _the discussion board: https://github.com/beetbox/beets/discussions/ diff --git a/docs/index.rst b/docs/index.rst index 961c0f42d..2ad2fca06 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,7 +20,7 @@ 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://discourse.beets.io +.. _the discussion board: https://github.com/beetbox/beets/discussions/ Contents -------- diff --git a/setup.py b/setup.py index c96f1fe24..e55a48e17 100755 --- a/setup.py +++ b/setup.py @@ -132,7 +132,7 @@ setup( ], "docs": [ "sphinx", - "sphinx_rtd_theme", + "pydata_sphinx_theme", ], # Plugin (optional) dependencies: "absubmit": ["requests"], diff --git a/test/plugins/test_edit.py b/test/plugins/test_edit.py index 04af56117..413db7cf6 100644 --- a/test/plugins/test_edit.py +++ b/test/plugins/test_edit.py @@ -72,8 +72,8 @@ class ModifyFileMocker: class EditMixin: """Helper containing some common functionality used for the Edit tests.""" - def assertItemFieldsModified( - self, library_items, items, fields=[], allowed=["path"] # noqa + def assertItemFieldsModified( # noqa + self, library_items, items, fields=[], allowed=["path"] ): """Assert that items in the library (`lib_items`) have different values on the specified `fields` (and *only* on those fields), compared to @@ -135,11 +135,11 @@ class EditCommandTest(unittest.TestCase, TestHelper, EditMixin): self.teardown_beets() self.unload_plugins() - def assertCounts( + def assertCounts( # noqa self, mock_write, album_count=ALBUM_COUNT, - track_count=TRACK_COUNT, # noqa + track_count=TRACK_COUNT, write_call_count=TRACK_COUNT, title_starts_with="", ): diff --git a/test/test_autotag.py b/test/test_autotag.py index c0268910d..130b69419 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -1078,9 +1078,9 @@ class EnumTest(_common.TestCase): """ def test_ordered_enum(self): - OrderedEnumClass = match.OrderedEnum( + OrderedEnumClass = match.OrderedEnum( # noqa "OrderedEnumTest", ["a", "b", "c"] - ) # noqa + ) self.assertLess(OrderedEnumClass.a, OrderedEnumClass.b) self.assertLess(OrderedEnumClass.a, OrderedEnumClass.c) self.assertLess(OrderedEnumClass.b, OrderedEnumClass.c) diff --git a/test/test_datequery.py b/test/test_datequery.py index 50066af66..73175f9ec 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -132,9 +132,9 @@ class DateIntervalTest(unittest.TestCase): self.assertContains("..", date=datetime.min) self.assertContains("..", "1000-01-01T00:00:00") - def assertContains( + def assertContains( # noqa self, interval_pattern, date_pattern=None, date=None - ): # noqa + ): if date is None: date = _date(date_pattern) (start, end) = _parse_periods(interval_pattern) diff --git a/tox.ini b/tox.ini index 4fa8d897c..fb0027177 100644 --- a/tox.ini +++ b/tox.ini @@ -33,7 +33,9 @@ commands = [testenv:docs] basepython = python3.10 -deps = sphinx<4.4.0 +deps = + sphinx<4.4.0 + pydata_sphinx_theme commands = sphinx-build -W -q -b html docs {envtmpdir}/html {posargs} # checks all links in the docs