Skip to content

Commit

Permalink
Merge master back in to fork
Browse files Browse the repository at this point in the history
  • Loading branch information
jackwilsdon committed Apr 28, 2016
2 parents 85fd608 + 48fff93 commit 7c9440c
Show file tree
Hide file tree
Showing 40 changed files with 322 additions and 143 deletions.
12 changes: 8 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
.. image:: https://travis-ci.org/beetbox/beets.svg?branch=master
:target: https://travis-ci.org/beetbox/beets
.. image:: http://img.shields.io/pypi/v/beets.svg
:target: https://pypi.python.org/pypi/beets

.. image:: https://img.shields.io/pypi/dw/beets.svg
:target: https://pypi.python.org/pypi/beets#downloads

.. image:: http://img.shields.io/codecov/c/github/beetbox/beets.svg
:target: https://codecov.io/github/beetbox/beets

.. image:: http://img.shields.io/pypi/v/beets.svg
:target: https://pypi.python.org/pypi/beets
.. image:: https://travis-ci.org/beetbox/beets.svg?branch=master
:target: https://travis-ci.org/beetbox/beets


Beets is the media library management system for obsessive-compulsive music
geeks.
Expand Down
2 changes: 1 addition & 1 deletion beets/autotag/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def __init__(self):
self._penalties = {}

@LazyClassProperty
def _weights(cls):
def _weights(cls): # noqa
"""A dictionary from keys to floating-point weights.
"""
weights_view = config['match']['distance_weights']
Expand Down
8 changes: 4 additions & 4 deletions beets/dbcore/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,13 @@ def _check_db(self, need_id=True):
# Essential field accessors.

@classmethod
def _type(self, key):
def _type(cls, key):
"""Get the type of a field, a `Type` instance.
If the field has no explicit type, it is given the base `Type`,
which does no conversion.
"""
return self._fields.get(key) or self._types.get(key) or types.DEFAULT
return cls._fields.get(key) or cls._types.get(key) or types.DEFAULT

def __getitem__(self, key):
"""Get the value for a field. Raise a KeyError if the field is
Expand Down Expand Up @@ -274,11 +274,11 @@ def keys(self, computed=False):
return base_keys

@classmethod
def all_keys(self):
def all_keys(cls):
"""Get a list of available keys for objects of this type.
Includes fixed and computed fields.
"""
return list(self._fields) + self._getters().keys()
return list(cls._fields) + cls._getters().keys()

# Act like a dictionary.

Expand Down
6 changes: 3 additions & 3 deletions beets/dbcore/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ def col_clause(self):
return self.field + " IS NULL", ()

@classmethod
def match(self, item):
def match(cls, item):
try:
return item[self.field] is None
return item[cls.field] is None
except KeyError:
return True

Expand Down Expand Up @@ -841,7 +841,7 @@ def is_slow(self):

class NullSort(Sort):
"""No sorting. Leave results unsorted."""
def sort(items):
def sort(self, items):
return items

def __nonzero__(self):
Expand Down
29 changes: 29 additions & 0 deletions beets/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,35 @@ def tmpl_aunique(self, keys=None, disam=None):
self.lib._memotable[memokey] = res
return res

@staticmethod
def tmpl_first(s, count=1, skip=0, sep=u'; ', join_str=u'; '):
""" Gets the item(s) from x to y in a string separated by something
and join then with something
:param s: the string
:param count: The number of items included
:param skip: The number of items skipped
:param sep: the separator. Usually is '; ' (default) or '/ '
:param join_str: the string which will join the items, default '; '.
"""
skip = int(skip)
count = skip + int(count)
return join_str.join(s.split(sep)[skip:count])

def tmpl_ifdef(self, field, trueval=u'', falseval=u''):
""" If field exists return trueval or the field (default)
otherwise, emit return falseval (if provided).
:param field: The name of the field
:param trueval: The string if the condition is true
:param falseval: The string if the condition is false
:return: The string, based on condition
"""
if self.item.formatted().get(field):
return trueval if trueval else self.item.formatted().get(field)
else:
return falseval


# Get the name of tmpl_* functions in the above class.
DefaultTemplateFunctions._func_names = \
Expand Down
2 changes: 1 addition & 1 deletion beets/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class BeetsLogger(ThreadLocalLevelLogger, StrFormatLogger):
my_manager.loggerClass = BeetsLogger


def getLogger(name=None):
def getLogger(name=None): # noqa
if name:
return my_manager.getLogger(name)
else:
Expand Down
11 changes: 10 additions & 1 deletion beets/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ class UserError(Exception):


# Encoding utilities.
def _in_encoding():
"""Get the encoding to use for *inputting* strings to the console.
"""
try:
return sys.stdin.encoding or 'utf-8'
except LookupError:
# TODO: create user config
return 'utf-8'


def _out_encoding():
"""Get the encoding to use for *outputting* strings to the console.
Expand Down Expand Up @@ -193,7 +202,7 @@ def input_(prompt=None):
except EOFError:
raise UserError(u'stdin stream ended while input required')

return resp.decode(sys.stdin.encoding or 'utf8', 'ignore')
return resp.decode(_in_encoding(), 'ignore')


def input_options(options, require=False, prompt=None, fallback_prompt=None,
Expand Down
22 changes: 11 additions & 11 deletions beets/util/artresizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,15 @@ class Shareable(type):
lazily-created shared instance of ``MyClass`` while calling
``MyClass()`` to construct a new object works as usual.
"""
def __init__(cls, name, bases, dict):
super(Shareable, cls).__init__(name, bases, dict)
cls._instance = None
def __init__(self, name, bases, dict):
super(Shareable, self).__init__(name, bases, dict)
self._instance = None

@property
def shared(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
def shared(self):
if self._instance is None:
self._instance = self()
return self._instance


class ArtResizer(object):
Expand Down Expand Up @@ -218,18 +218,18 @@ def _can_compare(self):
@staticmethod
def _check_method():
"""Return a tuple indicating an available method and its version."""
version = has_IM()
version = get_im_version()
if version:
return IMAGEMAGICK, version

version = has_PIL()
version = get_pil_version()
if version:
return PIL, version

return WEBPROXY, (0)


def has_IM():
def get_im_version():
"""Return Image Magick version or None if it is unavailable
Try invoking ImageMagick's "convert"."""
try:
Expand All @@ -248,7 +248,7 @@ def has_IM():
return None


def has_PIL():
def get_pil_version():
"""Return Image Magick version or None if it is unavailable
Try importing PIL."""
try:
Expand Down
2 changes: 1 addition & 1 deletion beets/util/confit.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def __repr__(self):
)

@classmethod
def of(self, value):
def of(cls, value):
"""Given either a dictionary or a `ConfigSource` object, return
a `ConfigSource` object. This lets a function accept either type
of object as an argument.
Expand Down
13 changes: 8 additions & 5 deletions beetsplug/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
from beets import plugins, ui


ASCII_DIGITS = string.digits + string.ascii_lowercase


class BucketError(Exception):
pass

Expand Down Expand Up @@ -155,23 +158,23 @@ def build_alpha_spans(alpha_spans_str, alpha_regexs):
[from...to]
"""
spans = []
ASCII_DIGITS = string.digits + string.ascii_lowercase

for elem in alpha_spans_str:
if elem in alpha_regexs:
spans.append(re.compile(alpha_regexs[elem]))
else:
bucket = sorted([x for x in elem.lower() if x.isalnum()])
if bucket:
beginIdx = ASCII_DIGITS.index(bucket[0])
endIdx = ASCII_DIGITS.index(bucket[-1])
begin_index = ASCII_DIGITS.index(bucket[0])
end_index = ASCII_DIGITS.index(bucket[-1])
else:
raise ui.UserError(u"invalid range defined for alpha bucket "
u"'%s': no alphanumeric character found" %
elem)
spans.append(
re.compile(
"^[" + ASCII_DIGITS[beginIdx:endIdx + 1] +
ASCII_DIGITS[beginIdx:endIdx + 1].upper() + "]"
"^[" + ASCII_DIGITS[begin_index:end_index + 1] +
ASCII_DIGITS[begin_index:end_index + 1].upper() + "]"
)
)
return spans
Expand Down
2 changes: 1 addition & 1 deletion beetsplug/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class ExportFormat(object):
"""The output format type"""

@classmethod
def factory(self, type, **kwargs):
def factory(cls, type, **kwargs):
if type == "json":
if kwargs['file_path']:
return JsonFileFormat(**kwargs)
Expand Down
52 changes: 31 additions & 21 deletions beetsplug/fetchart.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def get(self, album, extra):
raise NotImplementedError()

def _candidate(self, **kwargs):
return Candidate(source=self.NAME, log=self._log, **kwargs)
return Candidate(source=self, log=self._log, **kwargs)

def fetch_image(self, candidate, extra):
raise NotImplementedError()
Expand Down Expand Up @@ -395,12 +395,12 @@ def get(self, album, extra):
return

matches = []
# can there be more than one releasegroupid per responce?
for mb_releasegroupid in data.get(u'albums', dict()):
if album.mb_releasegroupid == mb_releasegroupid:
# note: there might be more art referenced, e.g. cdart
matches.extend(
data[u'albums'][mb_releasegroupid][u'albumcover'])
# can there be more than one releasegroupid per response?
for mbid, art in data.get(u'albums', dict()).items():
# there might be more art referenced, e.g. cdart, and an albumcover
# might not be present, even if the request was succesful
if album.mb_releasegroupid == mbid and u'albumcover' in art:
matches.extend(art[u'albumcover'])
# can this actually occur?
else:
self._log.debug(u'fanart.tv: unexpected mb_releasegroupid in '
Expand Down Expand Up @@ -454,7 +454,7 @@ class Wikipedia(RemoteArtSource):
NAME = u"Wikipedia (queried through DBpedia)"
DBPEDIA_URL = 'http://dbpedia.org/sparql'
WIKIPEDIA_URL = 'http://en.wikipedia.org/w/api.php'
SPARQL_QUERY = '''PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SPARQL_QUERY = u'''PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX dbpprop: <http://dbpedia.org/property/>
PREFIX owl: <http://dbpedia.org/ontology/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
Expand Down Expand Up @@ -660,9 +660,9 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
def __init__(self):
super(FetchArtPlugin, self).__init__()

# Holds paths to downloaded images between fetching them and
# placing them in the filesystem.
self.art_paths = {}
# Holds candidates corresponding to downloaded images between
# fetching them and placing them in the filesystem.
self.art_candidates = {}

self.config.add({
'auto': True,
Expand All @@ -675,7 +675,8 @@ def __init__(self):
'coverart', 'itunes', 'amazon', 'albumart'],
'google_key': None,
'google_engine': u'001442825323518660753:hrh5ch1gjzm',
'fanarttv_key': None
'fanarttv_key': None,
'store_source': False,
})
self.config['google_key'].redact = True
self.config['fanarttv_key'].redact = True
Expand Down Expand Up @@ -703,6 +704,7 @@ def __init__(self):
cover_names = self.config['cover_names'].as_str_seq()
self.cover_names = map(util.bytestring_path, cover_names)
self.cautious = self.config['cautious'].get(bool)
self.store_source = self.config['store_source'].get(bool)

self.src_removed = (config['import']['delete'].get(bool) or
config['import']['move'].get(bool))
Expand Down Expand Up @@ -753,19 +755,28 @@ def fetch_art(self, session, task):
candidate = self.art_for_album(task.album, task.paths, local)

if candidate:
self.art_paths[task] = candidate.path
self.art_candidates[task] = candidate

def _set_art(self, album, candidate, delete=False):
album.set_art(candidate.path, delete)
if self.store_source:
# store the source of the chosen artwork in a flexible field
self._log.debug(
u"Storing art_source for {0.albumartist} - {0.album}",
album)
album.art_source = SOURCE_NAMES[type(candidate.source)]
album.store()

# Synchronous; after music files are put in place.
def assign_art(self, session, task):
"""Place the discovered art in the filesystem."""
if task in self.art_paths:
path = self.art_paths.pop(task)
if task in self.art_candidates:
candidate = self.art_candidates.pop(task)

self._set_art(task.album, candidate, not self.src_removed)

album = task.album
album.set_art(path, not self.src_removed)
album.store()
if self.src_removed:
task.prune(path)
task.prune(candidate.path)

# Manual album art fetching.
def commands(self):
Expand Down Expand Up @@ -842,8 +853,7 @@ def batch_fetch_art(self, lib, albums, force):

candidate = self.art_for_album(album, local_paths)
if candidate:
album.set_art(candidate.path, False)
album.store()
self._set_art(album, candidate)
message = ui.colorize('text_success', u'found album art')
else:
message = ui.colorize('text_error', u'no art found')
Expand Down
6 changes: 3 additions & 3 deletions beetsplug/fuzzy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@

class FuzzyQuery(StringFieldQuery):
@classmethod
def string_match(self, pattern, val):
def string_match(cls, pattern, val):
# smartcase
if pattern.islower():
val = val.lower()
queryMatcher = difflib.SequenceMatcher(None, pattern, val)
query_matcher = difflib.SequenceMatcher(None, pattern, val)
threshold = config['fuzzy']['threshold'].as_number()
return queryMatcher.quick_ratio() >= threshold
return query_matcher.quick_ratio() >= threshold


class FuzzyPlugin(BeetsPlugin):
Expand Down
Loading

0 comments on commit 7c9440c

Please sign in to comment.