Here's another little experiment: to make flexattrs a little easier to use for
end users, you can now get and set them by using 'namespace-key' as the
argument to __getattr__ or __setattr__.
For example, try:
$ beet mod foo-bar=baz
$ beet ls -f '${foo-bar}'
baz
baz
baz
...
- Remove "part", "volume", "vol." multi-disc markers. These are often
part of album titles, and not necessarily indicative of a multi-disc
album. Only look for "CD X" and "disc X" (case insensitive), ignoring
white space and other non-word characters.
- Don't only expect each disc to be in a subdirectory of a common parent
directory, with all siblings belonging to the same release. Also match
any consecutive siblings (even when the parent contains other albums)
that are named with the same prefix and multi-disc marker.
- The `albums_in_dir(path)` function now always yields a list of paths
along with each list of items. `ItemTask.path` is now always a list of
paths.
- The `displayable_path(path)` function now accepts a list of paths, and
will join them with "; " by default. This can be changed with the
`separator` argument.
- The `sorted_walk()` function now does a case insensitive sort on
directories, but still returns case sensitive results. This allows
better multi-disc album detection.
- The `art_for_album()` function now takes a list of paths as its second
argument, instead of a single path.
This refactoring helps alleviate some of my own copypasta where we handle quiet
mode and its fallback. It has the added benefit of making none_rec_action work
for singletons as well as albums.
- Partial matches are always downgraded to a "medium" match.
- The config option, now called "default_action", lets you choose what to do
with "medium" matches.
- Expanded the "low" recommendation level to include cases with just one
match.
Add `import: none_rec_action` setting with a default of "ask" (current
behaviour). If set to "asis" or "skip", matches with no recommendation will be
imported as-is or skipped automatically.
Remove default option and require selection on confirmation prompts for:
- Partial matches, if `import: confirm_partial` setting is "yes".
- Matches that are below the medium recommendation threshold, but above the
gap threshold.
- Matches that have no recommendation.
- Matches other than the best and auto-suggested match.
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.
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).
The list_format_album and list_format_item options now *actually* affect the
display in commands other than "beet list". This replaces the -f/--format flags
-- if any users want to control this on the command line, we can reconsider this
decision.
Note that this involved passing around a "config" object, which we previously
haven't done. This seems a little bit messy, but configuration is about to
change entirely to be more like this style -- so this isn't a huge liability.
This version of the (renamed) _print_obj function uses introspection to
determine whether we're printing an Album or an Item. It's like function
overloading for Python! 😁
ImportSession will replace ImportConfig. It will have a *sane* number of fields
that are specific to the particular invocation of the importer -- e.g., lib and
paths. It also has a little bit of logic attached. Finally, it provides a method
for hooking the callbacks into the UI that is more elegant than assigning
callback functions -- OO inheritance to the rescue!
This adds a snapshot of the current Confit source (not a crime because Confit is
currently unreleased). It also changes around the bootstrapping mechanisms
enough to let "beet ls" run with the new Confit-based configuration. There's
much more to do.
This allows matches to indicate both missing and unmatched tracks in their
candidates and solves some of the spaghetti tuples that were passed around
during autotagging.
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.)
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.
- Copying and moving are mutually exclusive. Moving overrides copying so the
user only has to add one line ("import_move: true") to disable copying and
enable moving in its place.
- Deleting is only possible when copying.
- Deprecating the "delete" option (moving is almost always better).
- Removed command-line switch for moving. It's somewhat "unsafe", so this
removes some potential for accidental irreversible changes.
- Changelog & thanks.
- Update docs to refer to import_move instead of import_delete as the
correct solution for ending up with only one copy of the file.
"Skip" and "keep both" work already, but "remove old" does not. It sets a flag
on the import task object; this flag should then be read by the apply_choices
coroutine, where appropriate action should be taken as part of the same
transaction that adds stuff to the DB.
Unresolved questions:
- Should old files be deleted? (How is this decided? Based on config parameters,
or with another prompt option?)
- Logging details.
- Folder naming. If I have two albums with the same name, I want them in
different directories.
- Quiet mode. Currently, there are no checks -- the prompt should not be made in
quiet mode. (In that case, what should it do?)
- 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.
This commit disables the autoreject for incomplete albums. There is
several one-liner fixes in autotag/__init__.py and importer.py, as well
as some UI additions to report to the user when a track seems missing.
Only files which were modified after beets checked them will be checked again.
Implements feature request #227
--HG--
extra : transplant_source : K%F1d%C5%B1%1F%CA%AB%95ck%8C%AC%25m%F0%26%E4%9DB
"beet import -i" now tags items instead of albums. There are many loose ends to
tie up (marked with TODOs in the source):
- What to do about applying non-track metadata to matched tracks? Currently it's
just left in place.
- Plugin autotag candidates for tracks.
- No user querying yet.
- Non-autotagged -i import are unimplemented.
And, on top of those:
- Need to remove the action.TRACKS workflow and replace it with an option that
lets you jump over to the individual-track interface from the album tagger.
I'm shuffling around the feature-creeping importer code to keep it as
interface-agnostic as possible. The "importer" module now takes care of the
basic, increasingly complicated workflow while the ui.commands module is
relegated to containing actual user-interface stuff.
The import_resume option (nee import_progress) now exactly reflects the behavior
of -p and -P on the command line, which I think is way less confusing. That
option now has three settings: yes, no, and "ask" (the default). The "ask"
behavior cannot be specified on the command line, but I think that's OK. It's
also important to note that "no" means that progress is disabled entirely
(including saving progress for later resumes). The -q flag still overrides the
config option.
In the multithreaded version, the "directory done" state was written before
other progress states, causing it to be overwritten. This was because I had
stupidly put the "done" message in the initial generator, which of course
finishes before the entire pipeline finished. This manifested as two problems:
the tagger would always want to "resume" even when it had finished the last
time; "aBort"ing the process would not cause the next run to resume.
This makes the apply_choices coroutine run even for albums that are skipped or
still in the library. This (along with making things more predictable) lets the
apply_choices stage write the progress value as albums are retired even if they
are skipped.
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.
Previously, importing without autotagging just imported a bunch of Items. Now,
like the autotagging version, "import -A" creates albums based on the directory
hierarchy. The effect is exactly as if the user chose "use as-is" every time in
the interactive procedure. One side effect is that "import -A" can now only take
directories, where previously it could take single items on the command line. We
need a new solution for this kind of import in the future.