mirror of
https://github.com/beetbox/beets.git
synced 2026-01-03 14:32:55 +01:00
Merge branch 'master' of https://github.com/beetbox/beets into fix-bs1170gain
This commit is contained in:
commit
079e167b6b
22 changed files with 270 additions and 97 deletions
|
|
@ -19,7 +19,7 @@ import os
|
|||
|
||||
from beets.util import confit
|
||||
|
||||
__version__ = u'1.4.6'
|
||||
__version__ = u'1.4.7'
|
||||
__author__ = u'Adrian Sampson <adrian@radbox.org>'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ if util.SNI_SUPPORTED:
|
|||
else:
|
||||
BASE_URL = 'http://musicbrainz.org/'
|
||||
|
||||
NON_AUDIO_FORMATS = ['Data CD', 'DVD', 'DVD-Video', 'Blu-ray', 'HD-DVD', 'VCD',
|
||||
'SVCD', 'UMD', 'VHS']
|
||||
|
||||
SKIPPED_TRACKS = ['[data track]']
|
||||
|
||||
musicbrainzngs.set_useragent('beets', beets.__version__,
|
||||
'http://beets.io/')
|
||||
|
||||
|
|
@ -275,11 +280,24 @@ def album_info(release):
|
|||
disctitle = medium.get('title')
|
||||
format = medium.get('format')
|
||||
|
||||
if format in NON_AUDIO_FORMATS:
|
||||
continue
|
||||
|
||||
all_tracks = medium['track-list']
|
||||
if 'pregap' in medium:
|
||||
all_tracks.insert(0, medium['pregap'])
|
||||
|
||||
for track in all_tracks:
|
||||
|
||||
if ('title' in track['recording'] and
|
||||
track['recording']['title'] in SKIPPED_TRACKS):
|
||||
continue
|
||||
|
||||
if ('video' in track['recording'] and
|
||||
track['recording']['video'] == 'true' and
|
||||
config['match']['ignore_video_tracks']):
|
||||
continue
|
||||
|
||||
# Basic information from the recording.
|
||||
index += 1
|
||||
ti = track_info(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import:
|
|||
delete: no
|
||||
resume: ask
|
||||
incremental: no
|
||||
from_scratch: no
|
||||
quiet_fallback: skip
|
||||
none_rec_action: ask
|
||||
timid: no
|
||||
|
|
@ -125,5 +126,6 @@ match:
|
|||
original_year: no
|
||||
ignored: []
|
||||
required: []
|
||||
ignore_video_tracks: yes
|
||||
track_length_grace: 10
|
||||
track_length_max: 30
|
||||
|
|
|
|||
|
|
@ -534,6 +534,10 @@ class ImportTask(BaseImportTask):
|
|||
def apply_metadata(self):
|
||||
"""Copy metadata from match info to the items.
|
||||
"""
|
||||
if config['import']['from_scratch']:
|
||||
for item in self.match.mapping:
|
||||
item.clear()
|
||||
|
||||
autotag.apply_metadata(self.match.info, self.match.mapping)
|
||||
|
||||
def duplicate_items(self, lib):
|
||||
|
|
|
|||
|
|
@ -561,6 +561,11 @@ class Item(LibModel):
|
|||
if self.mtime == 0 and 'mtime' in values:
|
||||
self.mtime = values['mtime']
|
||||
|
||||
def clear(self):
|
||||
"""Set all key/value pairs to None."""
|
||||
for key in self._media_fields:
|
||||
setattr(self, key, None)
|
||||
|
||||
def get_album(self):
|
||||
"""Get the Album object that this item belongs to, if any, or
|
||||
None if the item is a singleton or is not associated with a
|
||||
|
|
|
|||
|
|
@ -1004,6 +1004,10 @@ import_cmd.parser.add_option(
|
|||
u'-I', u'--noincremental', dest='incremental', action='store_false',
|
||||
help=u'do not skip already-imported directories'
|
||||
)
|
||||
import_cmd.parser.add_option(
|
||||
u'--from-scratch', dest='from_scratch', action='store_true',
|
||||
help=u'erase existing metadata before applying new metadata'
|
||||
)
|
||||
import_cmd.parser.add_option(
|
||||
u'--flat', dest='flat', action='store_true',
|
||||
help=u'import an entire tree as a single album'
|
||||
|
|
|
|||
|
|
@ -88,14 +88,13 @@ def im_resize(maxwidth, path_in, path_out=None):
|
|||
log.debug(u'artresizer: ImageMagick resizing {0} to {1}',
|
||||
util.displayable_path(path_in), util.displayable_path(path_out))
|
||||
|
||||
# "-resize widthxheight>" shrinks images with dimension(s) larger
|
||||
# than the corresponding width and/or height dimension(s). The >
|
||||
# "only shrink" flag is prefixed by ^ escape char for Windows
|
||||
# compatibility.
|
||||
# "-resize WIDTHx>" shrinks images with the width larger
|
||||
# than the given width while maintaining the aspect ratio
|
||||
# with regards to the height.
|
||||
try:
|
||||
util.command_output([
|
||||
'convert', util.syspath(path_in, prefix=False),
|
||||
'-resize', '{0}x^>'.format(maxwidth),
|
||||
'-resize', '{0}x>'.format(maxwidth),
|
||||
util.syspath(path_out, prefix=False),
|
||||
])
|
||||
except subprocess.CalledProcessError:
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ class AcousticPlugin(plugins.BeetsPlugin):
|
|||
self.config.add({
|
||||
'auto': True,
|
||||
'force': False,
|
||||
'tags': []
|
||||
})
|
||||
|
||||
if self.config['auto']:
|
||||
|
|
@ -164,6 +165,7 @@ class AcousticPlugin(plugins.BeetsPlugin):
|
|||
def _fetch_info(self, items, write, force):
|
||||
"""Fetch additional information from AcousticBrainz for the `item`s.
|
||||
"""
|
||||
tags = self.config['tags'].as_str_seq()
|
||||
for item in items:
|
||||
# If we're not forcing re-downloading for all tracks, check
|
||||
# whether the data is already present. We use one
|
||||
|
|
@ -183,11 +185,18 @@ class AcousticPlugin(plugins.BeetsPlugin):
|
|||
data = self._get_data(item.mb_trackid)
|
||||
if data:
|
||||
for attr, val in self._map_data_to_scheme(data, ABSCHEME):
|
||||
self._log.debug(u'attribute {} of {} set to {}',
|
||||
attr,
|
||||
item,
|
||||
val)
|
||||
setattr(item, attr, val)
|
||||
if not tags or attr in tags:
|
||||
self._log.debug(u'attribute {} of {} set to {}',
|
||||
attr,
|
||||
item,
|
||||
val)
|
||||
setattr(item, attr, val)
|
||||
else:
|
||||
self._log.debug(u'skipping attribute {} of {}'
|
||||
u' (value {}) due to config',
|
||||
attr,
|
||||
item,
|
||||
val)
|
||||
item.store()
|
||||
if write:
|
||||
item.try_write()
|
||||
|
|
|
|||
|
|
@ -253,20 +253,19 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
"completeness" (objects with more non-null fields come first)
|
||||
and Albums are ordered by their track count.
|
||||
"""
|
||||
if tiebreak:
|
||||
kind = 'items' if all(isinstance(o, Item)
|
||||
for o in objs) else 'albums'
|
||||
kind = 'items' if all(isinstance(o, Item) for o in objs) else 'albums'
|
||||
|
||||
if tiebreak and kind in tiebreak.keys():
|
||||
key = lambda x: tuple(getattr(x, k) for k in tiebreak[kind])
|
||||
else:
|
||||
kind = Item if all(isinstance(o, Item) for o in objs) else Album
|
||||
if kind is Item:
|
||||
if kind == 'items':
|
||||
def truthy(v):
|
||||
# Avoid a Unicode warning by avoiding comparison
|
||||
# between a bytes object and the empty Unicode
|
||||
# string ''.
|
||||
return v is not None and \
|
||||
(v != '' if isinstance(v, six.text_type) else True)
|
||||
fields = kind.all_keys()
|
||||
fields = Item.all_keys()
|
||||
key = lambda x: sum(1 for f in fields if truthy(getattr(x, f)))
|
||||
else:
|
||||
key = lambda x: len(x.items())
|
||||
|
|
|
|||
|
|
@ -27,30 +27,21 @@ import six
|
|||
|
||||
# Filename field extraction patterns.
|
||||
PATTERNS = [
|
||||
# "01 - Track 01" and "01": do nothing
|
||||
r'^(\d+)\s*-\s*track\s*\d$',
|
||||
r'^\d+$',
|
||||
|
||||
# Useful patterns.
|
||||
r'^(?P<artist>.+)-(?P<title>.+)-(?P<tag>.*)$',
|
||||
r'^(?P<track>\d+)\s*-(?P<artist>.+)-(?P<title>.+)-(?P<tag>.*)$',
|
||||
r'^(?P<track>\d+)\s(?P<artist>.+)-(?P<title>.+)-(?P<tag>.*)$',
|
||||
r'^(?P<artist>.+)-(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\.\s*(?P<artist>.+)-(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\s*-\s*(?P<artist>.+)-(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\s*-(?P<artist>.+)-(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\s(?P<artist>.+)-(?P<title>.+)$',
|
||||
r'^(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\.\s*(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\s*-\s*(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\s(?P<title>.+)$',
|
||||
r'^(?P<title>.+) by (?P<artist>.+)$',
|
||||
# Useful patterns.
|
||||
r'^(?P<artist>.+)[\-_](?P<title>.+)[\-_](?P<tag>.*)$',
|
||||
r'^(?P<track>\d+)[\s.\-_]+(?P<artist>.+)[\-_](?P<title>.+)[\-_](?P<tag>.*)$',
|
||||
r'^(?P<artist>.+)[\-_](?P<title>.+)$',
|
||||
r'^(?P<track>\d+)[\s.\-_]+(?P<artist>.+)[\-_](?P<title>.+)$',
|
||||
r'^(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)[\s.\-_]+(?P<title>.+)$',
|
||||
r'^(?P<track>\d+)\s+(?P<title>.+)$',
|
||||
r'^(?P<title>.+) by (?P<artist>.+)$',
|
||||
r'^(?P<track>\d+).*$',
|
||||
]
|
||||
|
||||
# Titles considered "empty" and in need of replacement.
|
||||
BAD_TITLE_PATTERNS = [
|
||||
r'^$',
|
||||
r'\d+?\s?-?\s*track\s*\d+',
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,66 +1,97 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
1.4.6 (in development)
|
||||
1.4.7 (in development)
|
||||
----------------------
|
||||
|
||||
New features:
|
||||
Changelog goes here!
|
||||
|
||||
* When the importer finds duplicate albums, you can now merge all the tracks
|
||||
together and try importing them as a single album.
|
||||
Fixes:
|
||||
|
||||
* Non-audio media (DVD-Video, etc.) are now skipped by the autotagger. :bug:`2688`
|
||||
* Non-audio tracks (data tracks, video tracks, etc.) are now skipped by the
|
||||
autotagger. :bug:`1210`
|
||||
|
||||
|
||||
1.4.6 (December 21, 2017)
|
||||
-------------------------
|
||||
|
||||
The highlight of this release is "album merging," an oft-requested option in
|
||||
the importer to add new tracks to an existing album you already have in your
|
||||
library. This way, you no longer need to resort to removing the partial album
|
||||
from your library, combining the files manually, and importing again.
|
||||
|
||||
Here are the larger new features in this release:
|
||||
|
||||
* When the importer finds duplicate albums, you can now merge all the
|
||||
tracks---old and new---together and try importing them as a single, combined
|
||||
album.
|
||||
Thanks to :user:`udiboy1209`.
|
||||
:bug:`112` :bug:`2725`
|
||||
* :doc:`/plugins/lyrics`: The plugin can now produce reStructuredText files
|
||||
for beautiful, readable books of lyrics. Thanks to :user:`anarcat`.
|
||||
:bug:`2628`
|
||||
* :doc:`/plugins/convert`: Adds ``no_convert`` option which ignores transcoding
|
||||
items matching provided query string. Thanks to :user:`Stunner`.
|
||||
* A new :ref:`from_scratch` configuration option makes the importer remove old
|
||||
metadata before applying new metadata. This new feature complements the
|
||||
:doc:`zero </plugins/zero>` and :doc:`scrub </plugins/scrub>` plugins but is
|
||||
slightly different: beets clears out all the old tags it knows about and
|
||||
only keeps the new data it gets from the remote metadata source.
|
||||
Thanks to :user:`tummychow`.
|
||||
:bug:`934` :bug:`2755`
|
||||
|
||||
There are also somewhat littler, but still great, new features:
|
||||
|
||||
* :doc:`/plugins/convert`: A new ``no_convert`` option lets you skip
|
||||
transcoding items matching a query. Instead, the files are just copied
|
||||
as-is. Thanks to :user:`Stunner`.
|
||||
:bug:`2732` :bug:`2751`
|
||||
* :doc:`/plugins/fetchart`: The plugin has now a quiet switch that will only
|
||||
display missing album arts. Thanks to :user:`euri10`.
|
||||
* :doc:`/plugins/fetchart`: A new quiet switch that only prints out messages
|
||||
when album art is missing.
|
||||
Thanks to :user:`euri10`.
|
||||
:bug:`2683`
|
||||
* :doc:`/plugins/mbcollection`: The plugin now supports a custom MusicBrainz
|
||||
collection via the ``collection`` configuration option.
|
||||
* :doc:`/plugins/mbcollection`: You can configure a custom MusicBrainz
|
||||
collection via the new ``collection`` configuration option.
|
||||
:bug:`2685`
|
||||
* :doc:`/plugins/mbcollection`: The plugin now supports removing albums
|
||||
from collections that are longer in the beets library.
|
||||
* :doc:`/plugins/mpdstats`: The plugin now updates song stats when MPD switches
|
||||
from a song to a stream and when it plays the same song consecutively.
|
||||
:bug:`2707`
|
||||
* :doc:`/plugins/mbcollection`: The collection update command can now remove
|
||||
albums from collections that are longer in the beets library.
|
||||
* :doc:`/plugins/fetchart`: The ``clearart`` command now asks for confirmation
|
||||
before touching your files.
|
||||
Thanks to :user:`konman2`.
|
||||
:bug:`2708` :bug:`2427`
|
||||
* :doc:`/plugins/lyrics`: The Genius backend should work again.
|
||||
Thanks to :user:`lmagno`.
|
||||
:bug:`2709`
|
||||
* :doc:`/plugins/mpdstats`: The plugin now correctly updates song statistics
|
||||
when MPD switches from a song to a stream and when it plays the same song
|
||||
multiple times consecutively.
|
||||
:bug:`2707`
|
||||
* :doc:`/plugins/acousticbrainz`: The plugin can now be configured to write only
|
||||
a specific list of tags.
|
||||
Thanks to :user:`woparry`.
|
||||
|
||||
Fixes:
|
||||
There are lots and lots of bug fixes:
|
||||
|
||||
* :doc:`/plugins/hook`: Fixed a problem whereby accessing non-string properties of
|
||||
objects such as item or album (e.g. item.track) would cause a crash.
|
||||
* :doc:`/plugins/hook`: Fixed a problem where accessing non-string properties
|
||||
of ``item`` or ``album`` (e.g., ``item.track``) would cause a crash.
|
||||
Thanks to :user:`broddo`.
|
||||
:bug:`2740`
|
||||
* :doc:`/plugins/play`: When ``relative_to`` is set, correctly emit relative paths
|
||||
even when querying for albums rather than tracks.
|
||||
* :doc:`/plugins/play`: When ``relative_to`` is set, the plugin correctly
|
||||
emits relative paths even when querying for albums rather than tracks.
|
||||
Thanks to :user:`j000`.
|
||||
:bug:`2702`
|
||||
* Prevent Python from warning about a ``BrokenPipeError`` being ignored even
|
||||
though we do take it into account. This was an issue when using beets in
|
||||
simple shell scripts.
|
||||
* We suppress a spurious Python warning about a ``BrokenPipeError`` being
|
||||
ignored. This was an issue when using beets in simple shell scripts.
|
||||
Thanks to :user:`Azphreal`.
|
||||
:bug:`2622` :bug:`2631`
|
||||
* :doc:`/plugins/replaygain`: Fix a regression in the previous release related
|
||||
to the new R128 tags. :bug:`2615` :bug:`2623`
|
||||
* :doc:`/plugins/lyrics`: The MusixMatch backend now detect and warns
|
||||
the user when blocked on the server. Thanks to
|
||||
:user:`anarcat`. :bug:`2634` :bug:`2632`
|
||||
* :doc:`/plugins/lyrics`: The MusixMatch backend now detects and warns
|
||||
when the server has blocked the client.
|
||||
Thanks to :user:`anarcat`. :bug:`2634` :bug:`2632`
|
||||
* :doc:`/plugins/importfeeds`: Fix an error on Python 3 in certain
|
||||
configurations. Thanks to :user:`djl`. :bug:`2467` :bug:`2658`
|
||||
* :doc:`/plugins/edit`: Fix a bug when editing items during a ``-L``
|
||||
re-import. Previously, diffs against against unrelated items could be
|
||||
shown or beets could crash with a traceback. :bug:`2659`
|
||||
* :doc:`/plugins/kodiupdate`: Fix server URL and add better error reporting.
|
||||
* :doc:`/plugins/edit`: Fix a bug when editing items during a re-import with
|
||||
the ``-L`` flag. Previously, diffs against against unrelated items could be
|
||||
shown or beets could crash. :bug:`2659`
|
||||
* :doc:`/plugins/kodiupdate`: Fix the server URL and add better error
|
||||
reporting.
|
||||
:bug:`2662`
|
||||
* Fixed a problem where "no-op" modifications would reset files' mtimes,
|
||||
resulting in unnecessary writes. This most prominently affected the
|
||||
|
|
@ -70,26 +101,43 @@ Fixes:
|
|||
Python 3 on Windows with non-ASCII filenames. :bug:`2671`
|
||||
* :doc:`/plugins/absubmit`: Fix an occasional crash on Python 3 when the AB
|
||||
analysis tool produced non-ASCII metadata. :bug:`2673`
|
||||
* :doc:`/plugins/duplicates`: Fix the `--key` command line option, which was
|
||||
* :doc:`/plugins/duplicates`: Use the default tiebreak for items or albums
|
||||
when the configuration only specifies a tiebreak for the other kind of
|
||||
entity.
|
||||
Thanks to :user:`cgevans`.
|
||||
:bug:`2758`
|
||||
* :doc:`/plugins/duplicates`: Fix the ``--key`` command line option, which was
|
||||
ignored.
|
||||
* :doc:`/plugins/replaygain`: Fix album replaygain calculation with the
|
||||
gstreamer backend. :bug:`2636`
|
||||
* :doc:`/plugins/replaygain`: Fix album ReplayGain calculation with the
|
||||
GStreamer backend. :bug:`2636`
|
||||
* :doc:`/plugins/scrub`: Handle errors when manipulating files using newer
|
||||
versions of Mutagen. :bug:`2716`
|
||||
* :doc:`/plugins/fetchart`: Fix: don't skip running the fetchart plugin during import, when the
|
||||
"Edit Candidates" option is used. :bug:`2734`
|
||||
* :doc:`/plugins/fetchart`: The plugin no longer gets skipped during import
|
||||
when the "Edit Candidates" option is used from the :doc:`/plugins/edit`.
|
||||
:bug:`2734`
|
||||
* Fix a crash when numeric metadata fields contain just a minus or plus sign
|
||||
with no following numbers. Thanks to :user:`eigengrau`. :bug:`2741`
|
||||
* :doc:`/plugins/fromfilename`: Recognize file names that contain *only* a
|
||||
track number, such as `01.mp3`. Also, the plugin now allows underscores as a
|
||||
separator between fields.
|
||||
Thanks to :user:`Vrihub`.
|
||||
:bug:`2738` :bug:`2759`
|
||||
* Fixed an issue where images would be resized according to their longest
|
||||
edge, instead of their width, when using the ``maxwidth`` config option in
|
||||
the :doc:`/plugins/fetchart` and :doc:`/plugins/embedart`. Thanks to
|
||||
:user:`sekjun9878`. :bug:`2729`
|
||||
|
||||
For developers:
|
||||
There are some changes for developers:
|
||||
|
||||
* Fixed fields in Album and Item objects are now more strict about translating
|
||||
* "Fixed fields" in Album and Item objects are now more strict about translating
|
||||
missing values into type-specific null-like values. This should help in
|
||||
cases where a string field is unexpectedly `None` sometimes instead of just
|
||||
showing up as an empty string. :bug:`2605`
|
||||
* Refactored the move functions in library.py and the `manipulate_files` function
|
||||
in importer.py to use a single parameter describing the file operation instead
|
||||
of multiple boolean flags. :bug:`2682`
|
||||
* Refactored the move functions the `beets.library` module and the
|
||||
`manipulate_files` function in `beets.importer` to use a single parameter
|
||||
describing the file operation instead of multiple Boolean flags.
|
||||
There is a new numerated type describing how to move, copy, or link files.
|
||||
:bug:`2682`
|
||||
|
||||
|
||||
1.4.5 (June 20, 2017)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ project = u'beets'
|
|||
copyright = u'2016, Adrian Sampson'
|
||||
|
||||
version = '1.4'
|
||||
release = '1.4.6'
|
||||
release = '1.4.7'
|
||||
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
|
|
|
|||
7
docs/modd.conf
Normal file
7
docs/modd.conf
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
**/*.rst {
|
||||
prep: make html
|
||||
}
|
||||
|
||||
_build/html/** {
|
||||
daemon: devd -m _build/html
|
||||
}
|
||||
|
|
@ -54,10 +54,12 @@ Configuration
|
|||
-------------
|
||||
|
||||
To configure the plugin, make a ``acousticbrainz:`` section in your
|
||||
configuration file. There is one option:
|
||||
configuration file. There are three options:
|
||||
|
||||
- **auto**: Enable AcousticBrainz during ``beet import``.
|
||||
Default: ``yes``.
|
||||
- **force**: Download AcousticBrainz data even for tracks that already have
|
||||
it.
|
||||
Default: ``no``.
|
||||
- **tags**: Which tags from the list above to set on your files.
|
||||
Default: [] (all)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Thumbnails Plugin
|
||||
==================
|
||||
|
||||
The ``thumbnails`` plugin creates thumbnails your for album folders with the
|
||||
The ``thumbnails`` plugin creates thumbnails for your album folders with the
|
||||
album cover. This works on freedesktop.org-compliant file managers such as
|
||||
Nautilus or Thunar, and is therefore POSIX-only.
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,12 @@ Optional command flags:
|
|||
time, when no subdirectories will be skipped. So consider enabling the
|
||||
``incremental`` configuration option.
|
||||
|
||||
* When beets applies metadata to your music, it will retain the value of any
|
||||
existing tags that weren't overwritten, and import them into the database. You
|
||||
may prefer to only use existing metadata for finding matches, and to erase it
|
||||
completely when new metadata is applied. You can enforce this behavior with
|
||||
the ``--from-scratch`` option, or the ``from_scratch`` configuration option.
|
||||
|
||||
* By default, beets will proceed without asking if it finds a very close
|
||||
metadata match. To disable this and have the importer ask you every time,
|
||||
use the ``-t`` (for *timid*) option.
|
||||
|
|
|
|||
|
|
@ -475,6 +475,15 @@ Either ``yes`` or ``no``, controlling whether imported directories are
|
|||
recorded and whether these recorded directories are skipped. This
|
||||
corresponds to the ``-i`` flag to ``beet import``.
|
||||
|
||||
.. _from_scratch:
|
||||
|
||||
from_scratch
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Either ``yes`` or ``no`` (default), controlling whether existing metadata is
|
||||
discarded when a match is applied. This corresponds to the ``--from_scratch``
|
||||
flag to ``beet import``.
|
||||
|
||||
quiet_fallback
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
|
@ -765,6 +774,17 @@ want to enforce to the ``required`` setting::
|
|||
|
||||
No tags are required by default.
|
||||
|
||||
.. _ignore_video_tracks:
|
||||
|
||||
ignore_video_tracks
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default, video tracks within a release will be ignored. If you want them to
|
||||
be included (for example if you would like to track the audio-only versions of
|
||||
the video tracks), set it to ``no``.
|
||||
|
||||
Default: ``yes``.
|
||||
|
||||
.. _path-format-config:
|
||||
|
||||
Path Format Configuration
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from livereload import Server, shell
|
||||
|
||||
server = Server()
|
||||
server.watch('*.rst', shell('make html'))
|
||||
server.serve(root='_build/html')
|
||||
|
|
@ -166,7 +166,7 @@ def rst2md(text):
|
|||
"""Use Pandoc to convert text from ReST to Markdown.
|
||||
"""
|
||||
pandoc = subprocess.Popen(
|
||||
['pandoc', '--from=rst', '--to=markdown', '--no-wrap'],
|
||||
['pandoc', '--from=rst', '--to=markdown', '--wrap=none'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
stdout, _ = pandoc.communicate(text.encode('utf-8'))
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -56,7 +56,7 @@ if 'sdist' in sys.argv:
|
|||
|
||||
setup(
|
||||
name='beets',
|
||||
version='1.4.6',
|
||||
version='1.4.7',
|
||||
description='music tagger and library organizer',
|
||||
author='Adrian Sampson',
|
||||
author_email='adrian@radbox.org',
|
||||
|
|
|
|||
|
|
@ -633,6 +633,17 @@ class ImportTest(_common.TestCase, ImportHelper):
|
|||
self.assert_file_in_lib(
|
||||
b'Applied Artist', b'Applied Album', b'Applied Title 1.mp3')
|
||||
|
||||
def test_apply_from_scratch_removes_other_metadata(self):
|
||||
config['import']['from_scratch'] = True
|
||||
|
||||
for mediafile in self.import_media:
|
||||
mediafile.genre = u'Tag Genre'
|
||||
mediafile.save()
|
||||
|
||||
self.importer.add_choice(importer.action.APPLY)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.items().get().genre, u'')
|
||||
|
||||
def test_apply_with_move_deletes_import(self):
|
||||
config['import']['move'] = True
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import mock
|
|||
|
||||
class MBAlbumInfoTest(_common.TestCase):
|
||||
def _make_release(self, date_str='2009', tracks=None, track_length=None,
|
||||
track_artist=False):
|
||||
track_artist=False, medium_format='FORMAT'):
|
||||
release = {
|
||||
'title': 'ALBUM TITLE',
|
||||
'id': 'ALBUM ID',
|
||||
|
|
@ -90,12 +90,12 @@ class MBAlbumInfoTest(_common.TestCase):
|
|||
release['medium-list'].append({
|
||||
'position': '1',
|
||||
'track-list': track_list,
|
||||
'format': 'FORMAT',
|
||||
'format': medium_format,
|
||||
'title': 'MEDIUM TITLE',
|
||||
})
|
||||
return release
|
||||
|
||||
def _make_track(self, title, tr_id, duration, artist=False):
|
||||
def _make_track(self, title, tr_id, duration, artist=False, video=False):
|
||||
track = {
|
||||
'title': title,
|
||||
'id': tr_id,
|
||||
|
|
@ -113,6 +113,8 @@ class MBAlbumInfoTest(_common.TestCase):
|
|||
'name': 'RECORDING ARTIST CREDIT',
|
||||
}
|
||||
]
|
||||
if video:
|
||||
track['video'] = 'true'
|
||||
return track
|
||||
|
||||
def test_parse_release_with_year(self):
|
||||
|
|
@ -324,6 +326,62 @@ class MBAlbumInfoTest(_common.TestCase):
|
|||
d = mb.album_info(release)
|
||||
self.assertEqual(d.data_source, 'MusicBrainz')
|
||||
|
||||
def test_skip_non_audio_dvd(self):
|
||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||
release = self._make_release(tracks=tracks, medium_format="DVD")
|
||||
d = mb.album_info(release)
|
||||
self.assertEqual(len(d.tracks), 0)
|
||||
|
||||
def test_skip_non_audio_dvd_video(self):
|
||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||
release = self._make_release(tracks=tracks, medium_format="DVD-Video")
|
||||
d = mb.album_info(release)
|
||||
self.assertEqual(len(d.tracks), 0)
|
||||
|
||||
def test_no_skip_dvd_audio(self):
|
||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||
release = self._make_release(tracks=tracks, medium_format="DVD-Audio")
|
||||
d = mb.album_info(release)
|
||||
self.assertEqual(len(d.tracks), 2)
|
||||
|
||||
def test_skip_data_track(self):
|
||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||
self._make_track('[data track]', 'ID DATA TRACK',
|
||||
100.0 * 1000.0),
|
||||
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||
release = self._make_release(tracks=tracks)
|
||||
d = mb.album_info(release)
|
||||
self.assertEqual(len(d.tracks), 2)
|
||||
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
||||
|
||||
def test_skip_video_tracks_by_default(self):
|
||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||
self._make_track('TITLE VIDEO', 'ID VIDEO', 100.0 * 1000.0,
|
||||
False, True),
|
||||
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||
release = self._make_release(tracks=tracks)
|
||||
d = mb.album_info(release)
|
||||
self.assertEqual(len(d.tracks), 2)
|
||||
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
||||
|
||||
def test_no_skip_video_tracks_if_configured(self):
|
||||
config['match']['ignore_video_tracks'] = False
|
||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||
self._make_track('TITLE VIDEO', 'ID VIDEO', 100.0 * 1000.0,
|
||||
False, True),
|
||||
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||
release = self._make_release(tracks=tracks)
|
||||
d = mb.album_info(release)
|
||||
self.assertEqual(len(d.tracks), 3)
|
||||
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||
self.assertEqual(d.tracks[1].title, 'TITLE VIDEO')
|
||||
self.assertEqual(d.tracks[2].title, 'TITLE TWO')
|
||||
|
||||
|
||||
class ParseIDTest(_common.TestCase):
|
||||
def test_parse_id_correct(self):
|
||||
|
|
|
|||
Loading…
Reference in a new issue