diff --git a/beetsplug/convert.py b/beetsplug/convert.py index 6bc07c287..82e62af62 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -514,17 +514,23 @@ class ConvertPlugin(BeetsPlugin): except subprocess.CalledProcessError: return - # Change the newly-imported database entry to point to the - # converted file. - source_path = item.path - item.path = dest - item.write() - item.read() # Load new audio information data. - item.store() + pretend = self.config['pretend'].get(bool) + quiet = self.config['quiet'].get(bool) - if self.config['delete_originals']: - self._log.info('Removing original file {0}', source_path) - util.remove(source_path, False) + if not pretend: + # Change the newly-imported database entry to point to the + # converted file. + source_path = item.path + item.path = dest + item.write() + item.read() # Load new audio information data. + item.store() + + if self.config['delete_originals']: + if not quiet: + self._log.info('Removing original file {0}', + source_path) + util.remove(source_path, False) def _cleanup(self, task, session): for path in task.old_paths: diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index d015e4201..8c950c521 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -157,6 +157,11 @@ class DiscogsPlugin(BeetsPlugin): if not self.discogs_client: return + if not album and not artist: + self._log.debug('Skipping Discogs query. Files missing album and ' + 'artist tags.') + return [] + if va_likely: query = album else: diff --git a/beetsplug/limit.py b/beetsplug/limit.py new file mode 100644 index 000000000..3942ced0f --- /dev/null +++ b/beetsplug/limit.py @@ -0,0 +1,101 @@ +# This file is part of beets. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Adds head/tail functionality to list/ls. + +1. Implemented as `lslimit` command with `--head` and `--tail` options. This is + the idiomatic way to use this plugin. +2. Implemented as query prefix `<` for head functionality only. This is the + composable way to use the plugin (plays nicely with anything that uses the + query language). +""" + +from beets.dbcore import FieldQuery +from beets.plugins import BeetsPlugin +from beets.ui import Subcommand, decargs, print_ +from collections import deque +from itertools import islice + + +def lslimit(lib, opts, args): + """Query command with head/tail.""" + + if (opts.head is not None) and (opts.tail is not None): + raise ValueError("Only use one of --head and --tail") + if (opts.head or opts.tail or 0) < 0: + raise ValueError("Limit value must be non-negative") + + query = decargs(args) + if opts.album: + objs = lib.albums(query) + else: + objs = lib.items(query) + + if opts.head is not None: + objs = islice(objs, opts.head) + elif opts.tail is not None: + objs = deque(objs, opts.tail) + + for obj in objs: + print_(format(obj)) + + +lslimit_cmd = Subcommand( + "lslimit", + help="query with optional head or tail" +) + +lslimit_cmd.parser.add_option( + "--head", + action="store", + type="int", + default=None +) + +lslimit_cmd.parser.add_option( + "--tail", + action="store", + type="int", + default=None +) + +lslimit_cmd.parser.add_all_common_options() +lslimit_cmd.func = lslimit + + +class LimitPlugin(BeetsPlugin): + """Query limit functionality via command and query prefix.""" + + def commands(self): + """Expose `lslimit` subcommand.""" + return [lslimit_cmd] + + def queries(self): + + class HeadQuery(FieldQuery): + """This inner class pattern allows the query to track state.""" + n = 0 + N = None + + @classmethod + def value_match(cls, pattern, value): + if cls.N is None: + cls.N = int(pattern) + if cls.N < 0: + raise ValueError("Limit value must be non-negative") + cls.n += 1 + return cls.n <= cls.N + + return { + "<": HeadQuery + } diff --git a/beetsplug/lyrics.py b/beetsplug/lyrics.py index 7d026def1..1f215df45 100644 --- a/beetsplug/lyrics.py +++ b/beetsplug/lyrics.py @@ -419,11 +419,17 @@ class Genius(Backend): lyrics_div = verse_div.parent for br in lyrics_div.find_all("br"): br.replace_with("\n") + ads = lyrics_div.find_all("div", class_=re.compile("InreadAd__Container")) for ad in ads: ad.replace_with("\n") + footers = lyrics_div.find_all("div", + class_=re.compile("Lyrics__Footer")) + for footer in footers: + footer.replace_with("") + return lyrics_div.get_text() @@ -488,11 +494,11 @@ class Tekstowo(Backend): if not soup: return None - lyrics_div = soup.find("div", class_="song-text") + lyrics_div = soup.select("div.song-text > div.inner-text") if not lyrics_div: return None - return lyrics_div.get_text() + return lyrics_div[0].get_text() def remove_credits(text): diff --git a/beetsplug/web/__init__.py b/beetsplug/web/__init__.py index 240126e95..63f7f92ad 100644 --- a/beetsplug/web/__init__.py +++ b/beetsplug/web/__init__.py @@ -261,7 +261,7 @@ class QueryConverter(PathConverter): for query in queries] def to_url(self, value): - return ','.join([v.replace(os.sep, '\\') for v in value]) + return '/'.join([v.replace(os.sep, '\\') for v in value]) class EverythingConverter(PathConverter): diff --git a/docs/changelog.rst b/docs/changelog.rst index 51fbadb5e..71ef9973b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -22,7 +22,7 @@ Bug fixes: * :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 + 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 @@ -31,6 +31,18 @@ Bug fixes: * Fix a regression in the previous release that caused a `TypeError` when moving files across filesystems. :bug:`4168` +* :doc:`/plugins/convert`: Files are no longer converted when running import in + ``--pretend`` mode. +* :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 + 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 + (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 For packagers: @@ -38,6 +50,10 @@ For packagers: :bug:`4167` * The minimum required version of :pypi:`mediafile` is now 0.9.0. +Other new things: + +* :doc:`/plugins/limit`: Limit query results to head or tail (``lslimit`` + command only) 1.6.0 (November 27, 2021) ------------------------- diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index 40875b022..5aea1ae6b 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -19,7 +19,8 @@ authentication credentials via a personal access token or an OAuth2 authorization. Matches from Discogs will now show up during import alongside matches from -MusicBrainz. +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. diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 5ca8794fd..3d8b97606 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -98,6 +98,7 @@ following to your configuration:: kodiupdate lastgenre lastimport + limit loadext lyrics mbcollection diff --git a/docs/plugins/limit.rst b/docs/plugins/limit.rst new file mode 100644 index 000000000..ac8cc72c0 --- /dev/null +++ b/docs/plugins/limit.rst @@ -0,0 +1,58 @@ +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 ``'