A simple plugin that connects to the EchoNest API to retrieve
tempo (bpm) metadata for tracks. Functions similarly to the lyrics
plugin.
Requires the pyechonest library.
Instead of flac and lame the convert plugin now uses ffmpeg. This adds
support for more input formats and simplifies the code. ffmpeg also uses
the lame encoder internally and has equivalents of all the -V<num>
presets which should be sufficient.
We currently just document the fact that convert.exe can interfere with finding
ImageMagick's convert binary. We can solve this with a config option easily once
confit is merged.
This also changes the line endings for fetchart.rst back to Unix.
`urllib.urlretrieve` was using the correct extension in most cases -- I think
when the URL ended with .jpg -- but not in every case. This was leading to files
named just "cover" and not "cover.jpg" or something else sensible. In
particular, proxied URLs don't have .jpg extensions. This generates the filename
manually so the source image always has an extension.
Searching for `convert` or PIL has non-negligible performance overhead, so it's
preferable to only do it when really necessary. This way, the search is only
performed when ArtResizer.shared is accessed for the first time.
An earlier commit broke the call to art_for_album here (too few arguments).
I've also now propagated the maxwidth setting for the command to match the
import hook.
Fixed a number of issues with the changes to fetchart:
- Remove redundant fetches. This was making the Amazon source download every
image twice even when art resizing was not enabled!
- Restore local_only switch in plugin hook, which got lost in the shuffle at
some point.
- Don't replace the original image file in-place; use a temporary file instead.
This would clobber the original source image on the filesystem with the
downscaled version!
The previous method was to change self.__class__ dynamically to make __init__
instantiate different classes. This new way, which uses bare functions instead
of separate functor-like classes, instead just forwards the resize() call to
a module-global implementation based on self.method.
Additionally, the semantics of ArtResizer have changed. Clients now *always*
call resize() and proxy_url(), regardless of method. The method makes *one* of
these a no-op. This way, clients need not manually inspect which method is
being used.
artresizer.py instances an ArtResizer object that uses internally the PIL; ImageMagick
or a web proxy service to perform the resizing operations.
Because embedart works on input images located on filesystem it requires PIL or ImageMagick, whereas
fetchart is able to do the job with the fallback webproxy resizer.
Paging @yagebu: I think the old version of the code would embed album art into
the wrong file. Please correct me (and accept my apologies) if I'm wrong
though.
A user reported a problem with one of the logging statements where .format()
tried to convert a Unicode string to bytes because the log message was '', not
u''. As a rule, we should ensure that all logging statements use Unicode
literals.
With the new centralized print_obj function, we can greatly simplify the code
for the list command. This necessitated a couple of additional tweaks:
- For performance reasons, print_obj can now take a compiled template. (There's
still an issue with using the default/configured template, but we can cross
that bridge later).
- When listing albums, $path now expands to the album's item dir. So the format
string '$path' now exactly corresponds to passing the -p switch.
As an added bonus, we can now also reduce copypasta in the random plugin (which
behaves almost exactly the same as list).
This has been a long time coming, but we now finally keep track of ReplayGain
values in the database. This is an intermediate step toward a refactoring of the
RG plugin; at the moment, these values are not actually saved!
We now always calculate album gain when importing an album. This is "free" (no
performance cost) now and players are free to ignore the setting if they so
choose.
This ensures accurate album-level data. It also fixes a problem with the old way
of doing things where the MediaFiles and tool results would become misaligned if
a subset of the tracks needed recalculation.
Invocations of the mp3gain/aacgain commands are now wrapped in a centralized
function that takes care of output capture and error handling. This avoids code
duplication for the various sites at which the tool needs to be invoked.
This change also avoids unintentionally modifying tags via the command-line
tool. The "-s s" option makes the tool *just* calculate RG values rather than
toying with tags at all.
Eliminate the __subclasses__ trick for finding all plugins. Now we explicitly
look in each plugin module for a plugin class. This allows us to import plugin
modules with unintentionally loading them. This lets us reuse the image
embedding machinery without copypasta.
@tezoulbr: I'm changing these to debug messages partially so they don't print
out when running the tests (with nose, for example) but also because it could
get a little annoying for someone who *intends* to use the defaults for one of
these plugins. Let me know if you disagree.
This necessitated a slight refactoring in the plugin event handling mechanism.
Rather than loading all handlers up front and storing them in a module-scope
structure, we now scan for event handlers at every send(). This is probably
very slightly less efficient but allows for more flexible logic.
The Library object is now (almost) safe to share across threads. Per-thread
resources are now automatically managed internally. There is no longer any need
for the _reopen_lib hack to get multiple copies of the Library object.
This is the first of several commits that will modernize the beets codebase for
Python 2.6 conventions. (Compatibility with Python 2.5 is hereby abandoned.)
This commit makes rewrite explicitely match items using the .lower() function
instead of using Pythons builtin re.I flag.
This is required as the re.I flag only allows for case-independent pattern
matching with ascii chars. Even worse, the pattern is stored in lowercase when
using re.I, but the value to be matched isn't lowercased.
Example:
[rewrite]
artist Сергей Васильевич Рахманинов: Sergei Rachmaninoff
In an attempt to finally address the longstanding SQLite locking issues, I'm
introducing a way to explicitly, lexically scope transactions. The Transaction
class is a context manager that always fully fetches after SELECTs and
automatically commits on exit. No direct access to the library is allowed, so
all changes will eventually be committed and all queries will be completed. This
will also provide a debugging mechanism to show where concurrent transactions
are beginning and ending.
To support composition (transaction reentrancy), an internal, per-Library stack
of transactions is maintained. Commits only happen when the outermost
transaction exits. This means that, while it's possible to introduce atomicity
bugs by invoking Library methods outside of a transaction, you can conveniently
call them *without* a currently-active transaction to get a single atomic
action.
Note that this "transaction stack" concepts assumes a single Library object per
thread. Because we need to duplicate Library objects for concurrent access due
to sqlite3 limitation already, this is fine for now. Later, the interface should
provide one transaction stack per thread for shared Library objects.
Instead of parsing the template at each call to destination(), it's now possible
to parse them *once*, a priori, and re-use the resulting template object. This
is analogous to the re module's compiled expressions.
User can specify a fixed name (eg *import.m3u*) by setting the `m3u_fixedname`, otherwise a dynamic m3u filename will be generated base on the imported items (album/track title).
This is accomplished via a new event, "import_task_apply", which is called
right after metadata is applied to newly-imported items.
This change makes chroma REQUIRE a new version (0.6) of pyacoustid. Users with
older versions installed will see complaints about a missing method
"fingerprint_file".