This validator lets the user write either a real list, like [a, b, c], or just
a whitespace-separated string, like a b c. This is a little nicer for some
settings like "plugins" where the brackets and commas just look like line
noise.
We need plugins to set their config values at run time instead of module import
time. That is, defaults should be put in the __init__ method. This is easy
enough, but to make it even more convenient, I added a BeetsPlugin.config
field, which is a Confit view into a subsection of the configuration named
after the plugin.
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".
The old "caching"-based approach to fingerprinting was kinda hacky to begin
with. Now, the chroma plugin has an explicit opportunity (in the form of a new
event) to perform its initial fingerprinting and lookup for all tracks. Then,
this information is used explicitly during the autotagging phase rather than
being used transparently through memoization of the lookup function.
- Plugins are sent the unadulterated, None-ridden ordered items lists. Changed
the lastid plugin to accommodate this.
- Make colorization optional in partial album warnings.
- Fix some tests.
- Canonicalization is disabled by default. (This prevents pyyaml from being a
dependency if you don't use canonicalization.)
- Config value to set the tree file.
- Python style.
- Added YAML file to MANIFEST.in.
- Documentation.
The interface no longer specifies the type of the image embedded in the file; it
just returns a bytestring blob. When a type must be stored, it is inferred using
the imghdr module, which shoudl reduce the potential for weird bugs when the
formats don't correspond.
It turns out the documentation for MB was wrong here:
http://wiki.musicbrainz.org/XMLWebService#Searching_Tracks
At the time that I read it, at least, that said the field for track title was
named "title". It's actually named "track".
That is, in addition to contributing to the distance function, the plugin can
also influence the initial search into MusicBrainz. This will allow albums to be
tagged even when they're missing metadata entirely. This change required a
significant bit of refactoring to the "mb" module -- it's much nicer now.
This involves yet another new plugin method: album_distance. This leaves as the
last major puzzle piece for lastid the ability to augment the initial search
into MB (i.e., can start a search using fingerprinted metadata).
This required the introduction of a track_distance method on plugins. We'll also
need to add an album_distance method as well as a mechanism for extending the
search routine (so we can search for albums in MusicBrainz even when they have
no tags). This commit also adds the '-v' flag for printing debug logs (something
we should do more of).
This makes way more sense than fetching every metadata request from the
database. The performance of "beet ls -a" and the like should be drastically
better.
As part of this, the BaseLibrary class was also adapted to include a notion of
albums. This is reflected by the new BaseAlbum class, which the Album class
(formerly _AlbumInfo) completely replaces in the concrete Library. The BaseAlbum
class just fetches metadata from the underlying items.