mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 04:55:10 +01:00
Merge pull request #1887 from beetbox/no_unicode_literals
remove import of unicode_literals
This commit is contained in:
commit
abf2511002
130 changed files with 2232 additions and 2289 deletions
|
|
@ -1,10 +1,6 @@
|
|||
language: python
|
||||
sudo: false
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- python: 2.7
|
||||
|
|
|
|||
|
|
@ -13,15 +13,15 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
|
||||
import beets.library
|
||||
from beets.util import confit
|
||||
|
||||
__version__ = '1.3.18'
|
||||
__author__ = 'Adrian Sampson <adrian@radbox.org>'
|
||||
__version__ = u'1.3.18'
|
||||
__author__ = u'Adrian Sampson <adrian@radbox.org>'
|
||||
|
||||
Library = beets.library.Library
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Facilities for automatically determining files' correct metadata.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets import logging
|
||||
from beets import config
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""Glue between metadata sources and the matching logic."""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from collections import namedtuple
|
||||
import re
|
||||
|
|
@ -394,7 +393,7 @@ class Distance(object):
|
|||
"""
|
||||
if not isinstance(dist, Distance):
|
||||
raise ValueError(
|
||||
'`dist` must be a Distance object, not {0}'.format(type(dist))
|
||||
u'`dist` must be a Distance object, not {0}'.format(type(dist))
|
||||
)
|
||||
for key, penalties in dist._penalties.iteritems():
|
||||
self._penalties.setdefault(key, []).extend(penalties)
|
||||
|
|
@ -418,7 +417,7 @@ class Distance(object):
|
|||
"""
|
||||
if not 0.0 <= dist <= 1.0:
|
||||
raise ValueError(
|
||||
'`dist` must be between 0.0 and 1.0, not {0}'.format(dist)
|
||||
u'`dist` must be between 0.0 and 1.0, not {0}'.format(dist)
|
||||
)
|
||||
self._penalties.setdefault(key, []).append(dist)
|
||||
|
||||
|
|
@ -514,7 +513,7 @@ def album_for_mbid(release_id):
|
|||
try:
|
||||
album = mb.album_for_id(release_id)
|
||||
if album:
|
||||
plugins.send('albuminfo_received', info=album)
|
||||
plugins.send(u'albuminfo_received', info=album)
|
||||
return album
|
||||
except mb.MusicBrainzAPIError as exc:
|
||||
exc.log(log)
|
||||
|
|
@ -527,7 +526,7 @@ def track_for_mbid(recording_id):
|
|||
try:
|
||||
track = mb.track_for_id(recording_id)
|
||||
if track:
|
||||
plugins.send('trackinfo_received', info=track)
|
||||
plugins.send(u'trackinfo_received', info=track)
|
||||
return track
|
||||
except mb.MusicBrainzAPIError as exc:
|
||||
exc.log(log)
|
||||
|
|
@ -538,7 +537,7 @@ def albums_for_id(album_id):
|
|||
candidates = [album_for_mbid(album_id)]
|
||||
plugin_albums = plugins.album_for_id(album_id)
|
||||
for a in plugin_albums:
|
||||
plugins.send('albuminfo_received', info=a)
|
||||
plugins.send(u'albuminfo_received', info=a)
|
||||
candidates.extend(plugin_albums)
|
||||
return filter(None, candidates)
|
||||
|
||||
|
|
@ -548,7 +547,7 @@ def tracks_for_id(track_id):
|
|||
candidates = [track_for_mbid(track_id)]
|
||||
plugin_tracks = plugins.track_for_id(track_id)
|
||||
for t in plugin_tracks:
|
||||
plugins.send('trackinfo_received', info=t)
|
||||
plugins.send(u'trackinfo_received', info=t)
|
||||
candidates.extend(plugin_tracks)
|
||||
return filter(None, candidates)
|
||||
|
||||
|
|
@ -581,7 +580,7 @@ def album_candidates(items, artist, album, va_likely):
|
|||
|
||||
# Notify subscribed plugins about fetched album info
|
||||
for a in out:
|
||||
plugins.send('albuminfo_received', info=a)
|
||||
plugins.send(u'albuminfo_received', info=a)
|
||||
|
||||
return out
|
||||
|
||||
|
|
@ -605,6 +604,6 @@ def item_candidates(item, artist, title):
|
|||
|
||||
# Notify subscribed plugins about fetched track info
|
||||
for i in out:
|
||||
plugins.send('trackinfo_received', info=i)
|
||||
plugins.send(u'trackinfo_received', info=i)
|
||||
|
||||
return out
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
releases and tracks.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import datetime
|
||||
import re
|
||||
|
|
@ -337,7 +336,7 @@ def _add_candidate(items, results, info):
|
|||
|
||||
# Discard albums with zero tracks.
|
||||
if not info.tracks:
|
||||
log.debug('No tracks.')
|
||||
log.debug(u'No tracks.')
|
||||
return
|
||||
|
||||
# Don't duplicate.
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Searches for albums in the MusicBrainz database.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import musicbrainzngs
|
||||
import re
|
||||
|
|
@ -43,7 +42,7 @@ class MusicBrainzAPIError(util.HumanReadableException):
|
|||
def __init__(self, reason, verb, query, tb=None):
|
||||
self.query = query
|
||||
if isinstance(reason, musicbrainzngs.WebServiceError):
|
||||
reason = 'MusicBrainz not reachable'
|
||||
reason = u'MusicBrainz not reachable'
|
||||
super(MusicBrainzAPIError, self).__init__(reason, verb, tb)
|
||||
|
||||
def get_message(self):
|
||||
|
|
@ -162,7 +161,7 @@ def track_info(recording, index=None, medium=None, medium_index=None,
|
|||
medium=medium,
|
||||
medium_index=medium_index,
|
||||
medium_total=medium_total,
|
||||
data_source='MusicBrainz',
|
||||
data_source=u'MusicBrainz',
|
||||
data_url=track_url(recording['id']),
|
||||
)
|
||||
|
||||
|
|
@ -256,7 +255,7 @@ def album_info(release):
|
|||
mediums=len(release['medium-list']),
|
||||
artist_sort=artist_sort_name,
|
||||
artist_credit=artist_credit_name,
|
||||
data_source='MusicBrainz',
|
||||
data_source=u'MusicBrainz',
|
||||
data_url=album_url(release['id']),
|
||||
)
|
||||
info.va = info.artist_id == VARIOUS_ARTISTS_ID
|
||||
|
|
@ -377,7 +376,7 @@ def _parse_id(s):
|
|||
no ID can be found, return None.
|
||||
"""
|
||||
# Find the first thing that looks like a UUID/MBID.
|
||||
match = re.search('[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}', s)
|
||||
match = re.search(ur'[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}', s)
|
||||
if match:
|
||||
return match.group()
|
||||
|
||||
|
|
@ -398,7 +397,7 @@ def album_for_id(releaseid):
|
|||
log.debug(u'Album ID match failed.')
|
||||
return None
|
||||
except musicbrainzngs.MusicBrainzError as exc:
|
||||
raise MusicBrainzAPIError(exc, 'get release by ID', albumid,
|
||||
raise MusicBrainzAPIError(exc, u'get release by ID', albumid,
|
||||
traceback.format_exc())
|
||||
return album_info(res['release'])
|
||||
|
||||
|
|
@ -417,6 +416,6 @@ def track_for_id(releaseid):
|
|||
log.debug(u'Track ID match failed.')
|
||||
return None
|
||||
except musicbrainzngs.MusicBrainzError as exc:
|
||||
raise MusicBrainzAPIError(exc, 'get recording by ID', trackid,
|
||||
raise MusicBrainzAPIError(exc, u'get recording by ID', trackid,
|
||||
traceback.format_exc())
|
||||
return track_info(res['recording'])
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
"""DBCore is an abstract database package that forms the basis for beets'
|
||||
Library.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from .db import Model, Database
|
||||
from .query import Query, FieldQuery, MatchQuery, AndQuery, OrQuery
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""The central Model and Database constructs for DBCore.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import time
|
||||
import os
|
||||
|
|
@ -201,9 +200,11 @@ class Model(object):
|
|||
exception is raised otherwise.
|
||||
"""
|
||||
if not self._db:
|
||||
raise ValueError('{0} has no database'.format(type(self).__name__))
|
||||
raise ValueError(
|
||||
u'{0} has no database'.format(type(self).__name__)
|
||||
)
|
||||
if need_id and not self.id:
|
||||
raise ValueError('{0} has no id'.format(type(self).__name__))
|
||||
raise ValueError(u'{0} has no id'.format(type(self).__name__))
|
||||
|
||||
# Essential field accessors.
|
||||
|
||||
|
|
@ -255,11 +256,11 @@ class Model(object):
|
|||
del self._values_flex[key]
|
||||
self._dirty.add(key) # Mark for dropping on store.
|
||||
elif key in self._getters(): # Computed.
|
||||
raise KeyError('computed field {0} cannot be deleted'.format(key))
|
||||
raise KeyError(u'computed field {0} cannot be deleted'.format(key))
|
||||
elif key in self._fields: # Fixed.
|
||||
raise KeyError('fixed field {0} cannot be deleted'.format(key))
|
||||
raise KeyError(u'fixed field {0} cannot be deleted'.format(key))
|
||||
else:
|
||||
raise KeyError('no such field {0}'.format(key))
|
||||
raise KeyError(u'no such field {0}'.format(key))
|
||||
|
||||
def keys(self, computed=False):
|
||||
"""Get a list of available field names for this object. The
|
||||
|
|
@ -318,12 +319,12 @@ class Model(object):
|
|||
|
||||
def __getattr__(self, key):
|
||||
if key.startswith('_'):
|
||||
raise AttributeError('model has no attribute {0!r}'.format(key))
|
||||
raise AttributeError(u'model has no attribute {0!r}'.format(key))
|
||||
else:
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
raise AttributeError('no such field {0!r}'.format(key))
|
||||
raise AttributeError(u'no such field {0!r}'.format(key))
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if key.startswith('_'):
|
||||
|
|
@ -390,7 +391,7 @@ class Model(object):
|
|||
"""
|
||||
self._check_db()
|
||||
stored_obj = self._db._get(type(self), self.id)
|
||||
assert stored_obj is not None, "object {0} not in DB".format(self.id)
|
||||
assert stored_obj is not None, u"object {0} not in DB".format(self.id)
|
||||
self._values_fixed = {}
|
||||
self._values_flex = {}
|
||||
self.update(dict(stored_obj))
|
||||
|
|
@ -463,7 +464,7 @@ class Model(object):
|
|||
"""Parse a string as a value for the given key.
|
||||
"""
|
||||
if not isinstance(string, basestring):
|
||||
raise TypeError("_parse() argument must be a string")
|
||||
raise TypeError(u"_parse() argument must be a string")
|
||||
|
||||
return cls._type(key).parse(string)
|
||||
|
||||
|
|
@ -611,7 +612,7 @@ class Results(object):
|
|||
it.next()
|
||||
return it.next()
|
||||
except StopIteration:
|
||||
raise IndexError('result index {0} out of range'.format(n))
|
||||
raise IndexError(u'result index {0} out of range'.format(n))
|
||||
|
||||
def get(self):
|
||||
"""Return the first matching object, or None if no objects
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""The Query type hierarchy for DBCore.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
from operator import mul
|
||||
|
|
@ -39,7 +38,7 @@ class InvalidQueryError(ParsingError):
|
|||
def __init__(self, query, explanation):
|
||||
if isinstance(query, list):
|
||||
query = " ".join(query)
|
||||
message = "'{0}': {1}".format(query, explanation)
|
||||
message = u"'{0}': {1}".format(query, explanation)
|
||||
super(InvalidQueryError, self).__init__(message)
|
||||
|
||||
|
||||
|
|
@ -50,9 +49,9 @@ class InvalidQueryArgumentTypeError(ParsingError):
|
|||
query) InvalidQueryError can be raised.
|
||||
"""
|
||||
def __init__(self, what, expected, detail=None):
|
||||
message = "'{0}' is not {1}".format(what, expected)
|
||||
message = u"'{0}' is not {1}".format(what, expected)
|
||||
if detail:
|
||||
message = "{0}: {1}".format(message, detail)
|
||||
message = u"{0}: {1}".format(message, detail)
|
||||
super(InvalidQueryArgumentTypeError, self).__init__(message)
|
||||
|
||||
|
||||
|
|
@ -208,7 +207,7 @@ class RegexpQuery(StringFieldQuery):
|
|||
except re.error as exc:
|
||||
# Invalid regular expression.
|
||||
raise InvalidQueryArgumentTypeError(pattern,
|
||||
"a regular expression",
|
||||
u"a regular expression",
|
||||
format(exc))
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -283,7 +282,7 @@ class NumericQuery(FieldQuery):
|
|||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
raise InvalidQueryArgumentTypeError(s, "an int or a float")
|
||||
raise InvalidQueryArgumentTypeError(s, u"an int or a float")
|
||||
|
||||
def __init__(self, field, pattern, fast=True):
|
||||
super(NumericQuery, self).__init__(field, pattern, fast)
|
||||
|
|
@ -328,7 +327,7 @@ class NumericQuery(FieldQuery):
|
|||
elif self.rangemax is not None:
|
||||
return u'{0} <= ?'.format(self.field), (self.rangemax,)
|
||||
else:
|
||||
return '1', ()
|
||||
return u'1', ()
|
||||
|
||||
|
||||
class CollectionQuery(Query):
|
||||
|
|
@ -369,7 +368,7 @@ class CollectionQuery(Query):
|
|||
return clause, subvals
|
||||
|
||||
def __repr__(self):
|
||||
return "{0.__class__.__name__}({0.subqueries})".format(self)
|
||||
return "{0.__class__.__name__}({0.subqueries!r})".format(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return super(CollectionQuery, self).__eq__(other) and \
|
||||
|
|
@ -407,7 +406,7 @@ class AnyFieldQuery(CollectionQuery):
|
|||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return ("{0.__class__.__name__}({0.pattern!r}, {0.fields}, "
|
||||
return ("{0.__class__.__name__}({0.pattern!r}, {0.fields!r}, "
|
||||
"{0.query_class.__name__})".format(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
|
|
@ -467,7 +466,7 @@ class NotQuery(Query):
|
|||
return not self.subquery.match(item)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0.__class__.__name__}({0.subquery})".format(self)
|
||||
return "{0.__class__.__name__}({0.subquery!r})".format(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return super(NotQuery, self).__eq__(other) and \
|
||||
|
|
@ -535,7 +534,7 @@ class Period(object):
|
|||
precision (a string, one of "year", "month", or "day").
|
||||
"""
|
||||
if precision not in Period.precisions:
|
||||
raise ValueError('Invalid precision {0}'.format(precision))
|
||||
raise ValueError(u'Invalid precision {0}'.format(precision))
|
||||
self.date = date
|
||||
self.precision = precision
|
||||
|
||||
|
|
@ -575,7 +574,7 @@ class Period(object):
|
|||
elif 'day' == precision:
|
||||
return date + timedelta(days=1)
|
||||
else:
|
||||
raise ValueError('unhandled precision {0}'.format(precision))
|
||||
raise ValueError(u'unhandled precision {0}'.format(precision))
|
||||
|
||||
|
||||
class DateInterval(object):
|
||||
|
|
@ -587,7 +586,7 @@ class DateInterval(object):
|
|||
|
||||
def __init__(self, start, end):
|
||||
if start is not None and end is not None and not start < end:
|
||||
raise ValueError("start date {0} is not before end date {1}"
|
||||
raise ValueError(u"start date {0} is not before end date {1}"
|
||||
.format(start, end))
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
|
@ -608,7 +607,7 @@ class DateInterval(object):
|
|||
return True
|
||||
|
||||
def __str__(self):
|
||||
return'[{0}, {1})'.format(self.start, self.end)
|
||||
return '[{0}, {1})'.format(self.start, self.end)
|
||||
|
||||
|
||||
class DateQuery(FieldQuery):
|
||||
|
|
@ -677,7 +676,7 @@ class DurationQuery(NumericQuery):
|
|||
except ValueError:
|
||||
raise InvalidQueryArgumentTypeError(
|
||||
s,
|
||||
"a M:SS string or a float")
|
||||
u"a M:SS string or a float")
|
||||
|
||||
|
||||
# Sorting.
|
||||
|
|
@ -769,7 +768,7 @@ class MultipleSort(Sort):
|
|||
return items
|
||||
|
||||
def __repr__(self):
|
||||
return u'MultipleSort({0})'.format(repr(self.sorts))
|
||||
return 'MultipleSort({!r})'.format(self.sorts)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(tuple(self.sorts))
|
||||
|
|
@ -802,7 +801,7 @@ class FieldSort(Sort):
|
|||
return sorted(objs, key=key, reverse=not self.ascending)
|
||||
|
||||
def __repr__(self):
|
||||
return u'<{0}: {1}{2}>'.format(
|
||||
return '<{0}: {1}{2}>'.format(
|
||||
type(self).__name__,
|
||||
self.field,
|
||||
'+' if self.ascending else '-',
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Representation of type information for DBCore model fields.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from . import query
|
||||
from beets.util import str2bool
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
"""Provides the basic, interface-agnostic workflow for importing and
|
||||
autotagging music files.
|
||||
|
|
@ -251,17 +250,17 @@ class ImportSession(object):
|
|||
if duplicate:
|
||||
# Duplicate: log all three choices (skip, keep both, and trump).
|
||||
if task.should_remove_duplicates:
|
||||
self.tag_log('duplicate-replace', paths)
|
||||
self.tag_log(u'duplicate-replace', paths)
|
||||
elif task.choice_flag in (action.ASIS, action.APPLY):
|
||||
self.tag_log('duplicate-keep', paths)
|
||||
self.tag_log(u'duplicate-keep', paths)
|
||||
elif task.choice_flag is (action.SKIP):
|
||||
self.tag_log('duplicate-skip', paths)
|
||||
self.tag_log(u'duplicate-skip', paths)
|
||||
else:
|
||||
# Non-duplicate: log "skip" and "asis" choices.
|
||||
if task.choice_flag is action.ASIS:
|
||||
self.tag_log('asis', paths)
|
||||
self.tag_log(u'asis', paths)
|
||||
elif task.choice_flag is action.SKIP:
|
||||
self.tag_log('skip', paths)
|
||||
self.tag_log(u'skip', paths)
|
||||
|
||||
def should_resume(self, path):
|
||||
raise NotImplementedError
|
||||
|
|
@ -1150,7 +1149,7 @@ class ImportTaskFactory(object):
|
|||
if not (self.session.config['move'] or
|
||||
self.session.config['copy']):
|
||||
log.warn(u"Archive importing requires either "
|
||||
"'copy' or 'move' to be enabled.")
|
||||
u"'copy' or 'move' to be enabled.")
|
||||
return
|
||||
|
||||
log.debug(u'Extracting archive: {0}',
|
||||
|
|
@ -1325,7 +1324,7 @@ def resolve_duplicates(session, task):
|
|||
if task.choice_flag in (action.ASIS, action.APPLY, action.RETAG):
|
||||
found_duplicates = task.find_duplicates(session.lib)
|
||||
if found_duplicates:
|
||||
log.debug('found duplicates: {}'.format(
|
||||
log.debug(u'found duplicates: {}'.format(
|
||||
[o.id for o in found_duplicates]
|
||||
))
|
||||
session.resolve_duplicate(task, found_duplicates)
|
||||
|
|
@ -1342,7 +1341,7 @@ def import_asis(session, task):
|
|||
if task.skip:
|
||||
return
|
||||
|
||||
log.info('{}', displayable_path(task.paths))
|
||||
log.info(u'{}', displayable_path(task.paths))
|
||||
task.set_choice(action.ASIS)
|
||||
apply_choice(session, task)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""The core data store and collection logic for beets.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
|
@ -627,7 +626,7 @@ class Item(LibModel):
|
|||
self.write(path, tags)
|
||||
return True
|
||||
except FileOperationError as exc:
|
||||
log.error("{0}", exc)
|
||||
log.error(u"{0}", exc)
|
||||
return False
|
||||
|
||||
def try_sync(self, write, move, with_album=True):
|
||||
|
|
@ -647,7 +646,7 @@ class Item(LibModel):
|
|||
if move:
|
||||
# Check whether this file is inside the library directory.
|
||||
if self._db and self._db.directory in util.ancestry(self.path):
|
||||
log.debug('moving {0} to synchronize path',
|
||||
log.debug(u'moving {0} to synchronize path',
|
||||
util.displayable_path(self.path))
|
||||
self.move(with_album=with_album)
|
||||
self.store()
|
||||
|
|
@ -796,7 +795,7 @@ class Item(LibModel):
|
|||
if query == PF_KEY_DEFAULT:
|
||||
break
|
||||
else:
|
||||
assert False, "no default path format"
|
||||
assert False, u"no default path format"
|
||||
if isinstance(path_format, Template):
|
||||
subpath_tmpl = path_format
|
||||
else:
|
||||
|
|
@ -826,9 +825,12 @@ class Item(LibModel):
|
|||
if fellback:
|
||||
# Print an error message if legalization fell back to
|
||||
# default replacements because of the maximum length.
|
||||
log.warning('Fell back to default replacements when naming '
|
||||
'file {}. Configure replacements to avoid lengthening '
|
||||
'the filename.', subpath)
|
||||
log.warning(
|
||||
u'Fell back to default replacements when naming '
|
||||
u'file {}. Configure replacements to avoid lengthening '
|
||||
u'the filename.',
|
||||
subpath
|
||||
)
|
||||
|
||||
if fragment:
|
||||
return subpath
|
||||
|
|
@ -1016,7 +1018,7 @@ class Album(LibModel):
|
|||
"""
|
||||
item = self.items().get()
|
||||
if not item:
|
||||
raise ValueError('empty album')
|
||||
raise ValueError(u'empty album')
|
||||
return os.path.dirname(item.path)
|
||||
|
||||
def _albumtotal(self):
|
||||
|
|
@ -1170,7 +1172,7 @@ def parse_query_string(s, model_cls):
|
|||
|
||||
The string is split into components using shell-like syntax.
|
||||
"""
|
||||
assert isinstance(s, unicode), "Query is not unicode: {0!r}".format(s)
|
||||
assert isinstance(s, unicode), u"Query is not unicode: {0!r}".format(s)
|
||||
try:
|
||||
parts = util.shlex_split(s)
|
||||
except ValueError as exc:
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ that when getLogger(name) instantiates a logger that logger uses
|
|||
{}-style formatting.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from copy import copy
|
||||
from logging import * # noqa
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ Internally ``MediaFile`` uses ``MediaField`` descriptors to access the
|
|||
data from the tags. In turn ``MediaField`` uses a number of
|
||||
``StorageStyle`` strategies to handle format specific logic.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import mutagen
|
||||
import mutagen.mp3
|
||||
|
|
@ -650,13 +649,13 @@ class MP4BoolStorageStyle(MP4StorageStyle):
|
|||
return None
|
||||
|
||||
def get_list(self, mutagen_file):
|
||||
raise NotImplementedError('MP4 bool storage does not support lists')
|
||||
raise NotImplementedError(u'MP4 bool storage does not support lists')
|
||||
|
||||
def set(self, mutagen_file, value):
|
||||
mutagen_file[self.key] = value
|
||||
|
||||
def set_list(self, mutagen_file, values):
|
||||
raise NotImplementedError('MP4 bool storage does not support lists')
|
||||
raise NotImplementedError(u'MP4 bool storage does not support lists')
|
||||
|
||||
|
||||
class MP4ImageStorageStyle(MP4ListStorageStyle):
|
||||
|
|
@ -674,7 +673,7 @@ class MP4ImageStorageStyle(MP4ListStorageStyle):
|
|||
elif image.mime_type == 'image/jpeg':
|
||||
kind = mutagen.mp4.MP4Cover.FORMAT_JPEG
|
||||
else:
|
||||
raise ValueError('MP4 files only supports PNG and JPEG images')
|
||||
raise ValueError(u'MP4 files only supports PNG and JPEG images')
|
||||
return mutagen.mp4.MP4Cover(image.data, kind)
|
||||
|
||||
|
||||
|
|
@ -1364,11 +1363,11 @@ class MediaFile(object):
|
|||
# anywhere else.
|
||||
raise
|
||||
else:
|
||||
log.debug('{}', traceback.format_exc())
|
||||
log.debug(u'{}', traceback.format_exc())
|
||||
raise MutagenError(path, exc)
|
||||
except Exception as exc:
|
||||
# Isolate bugs in Mutagen.
|
||||
log.debug('{}', traceback.format_exc())
|
||||
log.debug(u'{}', traceback.format_exc())
|
||||
log.error(u'uncaught Mutagen exception in open: {0}', exc)
|
||||
raise MutagenError(path, exc)
|
||||
|
||||
|
|
@ -1441,7 +1440,7 @@ class MediaFile(object):
|
|||
# Propagate these through: they don't represent Mutagen bugs.
|
||||
raise
|
||||
except Exception as exc:
|
||||
log.debug('{}', traceback.format_exc())
|
||||
log.debug(u'{}', traceback.format_exc())
|
||||
log.error(u'uncaught Mutagen exception in save: {0}', exc)
|
||||
raise MutagenError(self.path, exc)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Support for beets plugins."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import inspect
|
||||
import traceback
|
||||
|
|
@ -267,7 +266,7 @@ def load_plugins(names=()):
|
|||
|
||||
except:
|
||||
log.warn(
|
||||
'** error loading plugin {}:\n{}',
|
||||
u'** error loading plugin {}:\n{}',
|
||||
name,
|
||||
traceback.format_exc(),
|
||||
)
|
||||
|
|
@ -322,8 +321,8 @@ def types(model_cls):
|
|||
if field in types and plugin_types[field] != types[field]:
|
||||
raise PluginConflictException(
|
||||
u'Plugin {0} defines flexible field {1} '
|
||||
'which has already been defined with '
|
||||
'another type.'.format(plugin.name, field)
|
||||
u'which has already been defined with '
|
||||
u'another type.'.format(plugin.name, field)
|
||||
)
|
||||
types.update(plugin_types)
|
||||
return types
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ interface. To invoke the CLI, just call beets.ui.main(). The actual
|
|||
CLI commands are implemented in the ui.commands module.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import locale
|
||||
import optparse
|
||||
|
|
@ -192,7 +191,7 @@ def input_(prompt=None):
|
|||
try:
|
||||
resp = raw_input()
|
||||
except EOFError:
|
||||
raise UserError('stdin stream ended while input required')
|
||||
raise UserError(u'stdin stream ended while input required')
|
||||
|
||||
return resp.decode(sys.stdin.encoding or 'utf8', 'ignore')
|
||||
|
||||
|
|
@ -238,7 +237,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None,
|
|||
found_letter = letter
|
||||
break
|
||||
else:
|
||||
raise ValueError('no unambiguous lettering found')
|
||||
raise ValueError(u'no unambiguous lettering found')
|
||||
|
||||
letters[found_letter.lower()] = option
|
||||
index = option.index(found_letter)
|
||||
|
|
@ -321,9 +320,9 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None,
|
|||
# Make a fallback prompt too. This is displayed if the user enters
|
||||
# something that is not recognized.
|
||||
if not fallback_prompt:
|
||||
fallback_prompt = 'Enter one of '
|
||||
fallback_prompt = u'Enter one of '
|
||||
if numrange:
|
||||
fallback_prompt += '%i-%i, ' % numrange
|
||||
fallback_prompt += u'%i-%i, ' % numrange
|
||||
fallback_prompt += ', '.join(display_letters) + ':'
|
||||
|
||||
resp = input_(prompt)
|
||||
|
|
@ -362,9 +361,9 @@ def input_yn(prompt, require=False):
|
|||
"yes" unless `require` is `True`, in which case there is no default.
|
||||
"""
|
||||
sel = input_options(
|
||||
('y', 'n'), require, prompt, 'Enter Y or N:'
|
||||
('y', 'n'), require, prompt, u'Enter Y or N:'
|
||||
)
|
||||
return sel == 'y'
|
||||
return sel == u'y'
|
||||
|
||||
|
||||
def input_select_objects(prompt, objs, rep):
|
||||
|
|
@ -376,18 +375,18 @@ def input_select_objects(prompt, objs, rep):
|
|||
object to print it out when confirming objects individually.
|
||||
"""
|
||||
choice = input_options(
|
||||
('y', 'n', 's'), False,
|
||||
'%s? (Yes/no/select)' % prompt)
|
||||
(u'y', u'n', u's'), False,
|
||||
u'%s? (Yes/no/select)' % prompt)
|
||||
print() # Blank line.
|
||||
|
||||
if choice == 'y': # Yes.
|
||||
if choice == u'y': # Yes.
|
||||
return objs
|
||||
|
||||
elif choice == 's': # Select.
|
||||
elif choice == u's': # Select.
|
||||
out = []
|
||||
for obj in objs:
|
||||
rep(obj)
|
||||
if input_yn('%s? (yes/no)' % prompt, True):
|
||||
if input_yn(u'%s? (yes/no)' % prompt, True):
|
||||
out.append(obj)
|
||||
print() # go to a new line
|
||||
return out
|
||||
|
|
@ -400,14 +399,14 @@ def input_select_objects(prompt, objs, rep):
|
|||
|
||||
def human_bytes(size):
|
||||
"""Formats size, a number of bytes, in a human-readable way."""
|
||||
powers = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'H']
|
||||
powers = [u'', u'K', u'M', u'G', u'T', u'P', u'E', u'Z', u'Y', u'H']
|
||||
unit = 'B'
|
||||
for power in powers:
|
||||
if size < 1024:
|
||||
return "%3.1f %s%s" % (size, power, unit)
|
||||
return u"%3.1f %s%s" % (size, power, unit)
|
||||
size /= 1024.0
|
||||
unit = 'iB'
|
||||
return "big"
|
||||
unit = u'iB'
|
||||
return u"big"
|
||||
|
||||
|
||||
def human_seconds(interval):
|
||||
|
|
@ -415,13 +414,13 @@ def human_seconds(interval):
|
|||
interval using English words.
|
||||
"""
|
||||
units = [
|
||||
(1, 'second'),
|
||||
(60, 'minute'),
|
||||
(60, 'hour'),
|
||||
(24, 'day'),
|
||||
(7, 'week'),
|
||||
(52, 'year'),
|
||||
(10, 'decade'),
|
||||
(1, u'second'),
|
||||
(60, u'minute'),
|
||||
(60, u'hour'),
|
||||
(24, u'day'),
|
||||
(7, u'week'),
|
||||
(52, u'year'),
|
||||
(10, u'decade'),
|
||||
]
|
||||
for i in range(len(units) - 1):
|
||||
increment, suffix = units[i]
|
||||
|
|
@ -434,7 +433,7 @@ def human_seconds(interval):
|
|||
increment, suffix = units[-1]
|
||||
interval /= float(increment)
|
||||
|
||||
return "%3.1f %ss" % (interval, suffix)
|
||||
return u"%3.1f %ss" % (interval, suffix)
|
||||
|
||||
|
||||
def human_seconds_short(interval):
|
||||
|
|
@ -495,7 +494,7 @@ def _colorize(color, text):
|
|||
elif color in LIGHT_COLORS:
|
||||
escape = COLOR_ESCAPE + "%i;01m" % (LIGHT_COLORS[color] + 30)
|
||||
else:
|
||||
raise ValueError('no such color %s', color)
|
||||
raise ValueError(u'no such color %s', color)
|
||||
return escape + text + RESET_COLOR
|
||||
|
||||
|
||||
|
|
@ -777,7 +776,7 @@ class CommonOptionsParser(optparse.OptionParser, object):
|
|||
Sets the album property on the options extracted from the CLI.
|
||||
"""
|
||||
album = optparse.Option(*flags, action='store_true',
|
||||
help='match albums instead of tracks')
|
||||
help=u'match albums instead of tracks')
|
||||
self.add_option(album)
|
||||
self._album_flags = set(flags)
|
||||
|
||||
|
|
@ -822,7 +821,7 @@ class CommonOptionsParser(optparse.OptionParser, object):
|
|||
callback=self._set_format,
|
||||
callback_kwargs={'fmt': '$path',
|
||||
'store_true': True},
|
||||
help='print paths for matched items or albums')
|
||||
help=u'print paths for matched items or albums')
|
||||
self.add_option(path)
|
||||
|
||||
def add_format_option(self, flags=('-f', '--format'), target=None):
|
||||
|
|
@ -850,7 +849,7 @@ class CommonOptionsParser(optparse.OptionParser, object):
|
|||
opt = optparse.Option(*flags, action='callback',
|
||||
callback=self._set_format,
|
||||
callback_kwargs=kwargs,
|
||||
help='print with custom format')
|
||||
help=u'print with custom format')
|
||||
self.add_option(opt)
|
||||
|
||||
def add_all_common_options(self):
|
||||
|
|
@ -916,7 +915,7 @@ class SubcommandsOptionParser(CommonOptionsParser):
|
|||
"""
|
||||
# A more helpful default usage.
|
||||
if 'usage' not in kwargs:
|
||||
kwargs['usage'] = """
|
||||
kwargs['usage'] = u"""
|
||||
%prog COMMAND [ARGS...]
|
||||
%prog help COMMAND"""
|
||||
kwargs['add_help_option'] = False
|
||||
|
|
@ -1024,7 +1023,7 @@ class SubcommandsOptionParser(CommonOptionsParser):
|
|||
cmdname = args.pop(0)
|
||||
subcommand = self._subcommand_for_name(cmdname)
|
||||
if not subcommand:
|
||||
raise UserError("unknown command '{0}'".format(cmdname))
|
||||
raise UserError(u"unknown command '{0}'".format(cmdname))
|
||||
|
||||
suboptions, subargs = subcommand.parse_args(args)
|
||||
return subcommand, suboptions, subargs
|
||||
|
|
@ -1076,7 +1075,7 @@ def _load_plugins(config):
|
|||
"""
|
||||
paths = config['pluginpath'].get(confit.StrSeq(split=False))
|
||||
paths = map(util.normpath, paths)
|
||||
log.debug('plugin paths: {0}', util.displayable_path(paths))
|
||||
log.debug(u'plugin paths: {0}', util.displayable_path(paths))
|
||||
|
||||
import beetsplug
|
||||
beetsplug.__path__ = paths + beetsplug.__path__
|
||||
|
|
@ -1146,10 +1145,13 @@ def _configure(options):
|
|||
old_key = 'list_format_{0}'.format(elem)
|
||||
if config[old_key].exists():
|
||||
new_key = 'format_{0}'.format(elem)
|
||||
log.warning('Warning: configuration uses "{0}" which is deprecated'
|
||||
' in favor of "{1}" now that it affects all commands. '
|
||||
'See changelog & documentation.'.format(old_key,
|
||||
new_key))
|
||||
log.warning(
|
||||
u'Warning: configuration uses "{0}" which is deprecated'
|
||||
u' in favor of "{1}" now that it affects all commands. '
|
||||
u'See changelog & documentation.',
|
||||
old_key,
|
||||
new_key,
|
||||
)
|
||||
config[new_key].set(config[old_key])
|
||||
|
||||
config_path = config.user_config_path()
|
||||
|
|
@ -1178,7 +1180,7 @@ def _open_library(config):
|
|||
)
|
||||
lib.get_item(0) # Test database connection.
|
||||
except (sqlite3.OperationalError, sqlite3.DatabaseError):
|
||||
log.debug('{}', traceback.format_exc())
|
||||
log.debug(u'{}', traceback.format_exc())
|
||||
raise UserError(u"database file {0} could not be opened".format(
|
||||
util.displayable_path(dbpath)
|
||||
))
|
||||
|
|
@ -1197,15 +1199,15 @@ def _raw_main(args, lib=None):
|
|||
parser.add_format_option(flags=('--format-item',), target=library.Item)
|
||||
parser.add_format_option(flags=('--format-album',), target=library.Album)
|
||||
parser.add_option('-l', '--library', dest='library',
|
||||
help='library database file to use')
|
||||
help=u'library database file to use')
|
||||
parser.add_option('-d', '--directory', dest='directory',
|
||||
help="destination music directory")
|
||||
help=u"destination music directory")
|
||||
parser.add_option('-v', '--verbose', dest='verbose', action='count',
|
||||
help='log more details (use twice for even more)')
|
||||
help=u'log more details (use twice for even more)')
|
||||
parser.add_option('-c', '--config', dest='config',
|
||||
help='path to configuration file')
|
||||
help=u'path to configuration file')
|
||||
parser.add_option('-h', '--help', dest='help', action='store_true',
|
||||
help='show this help message and exit')
|
||||
help=u'show this help message and exit')
|
||||
parser.add_option('--version', dest='version', action='store_true',
|
||||
help=optparse.SUPPRESS_HELP)
|
||||
|
||||
|
|
@ -1261,4 +1263,4 @@ def main(args=None):
|
|||
raise
|
||||
except KeyboardInterrupt:
|
||||
# Silently ignore ^C except in verbose mode.
|
||||
log.debug('{}', traceback.format_exc())
|
||||
log.debug(u'{}', traceback.format_exc())
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Miscellaneous utility functions."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
|
@ -57,10 +56,10 @@ class HumanReadableException(Exception):
|
|||
def _gerund(self):
|
||||
"""Generate a (likely) gerund form of the English verb.
|
||||
"""
|
||||
if ' ' in self.verb:
|
||||
if u' ' in self.verb:
|
||||
return self.verb
|
||||
gerund = self.verb[:-1] if self.verb.endswith('e') else self.verb
|
||||
gerund += 'ing'
|
||||
gerund = self.verb[:-1] if self.verb.endswith(u'e') else self.verb
|
||||
gerund += u'ing'
|
||||
return gerund
|
||||
|
||||
def _reasonstr(self):
|
||||
|
|
@ -415,7 +414,7 @@ def copy(path, dest, replace=False):
|
|||
path = syspath(path)
|
||||
dest = syspath(dest)
|
||||
if not replace and os.path.exists(dest):
|
||||
raise FilesystemError('file exists', 'copy', (path, dest))
|
||||
raise FilesystemError(u'file exists', 'copy', (path, dest))
|
||||
try:
|
||||
shutil.copyfile(path, dest)
|
||||
except (OSError, IOError) as exc:
|
||||
|
|
@ -436,7 +435,7 @@ def move(path, dest, replace=False):
|
|||
path = syspath(path)
|
||||
dest = syspath(dest)
|
||||
if os.path.exists(dest) and not replace:
|
||||
raise FilesystemError('file exists', 'rename', (path, dest),
|
||||
raise FilesystemError(u'file exists', 'rename', (path, dest),
|
||||
traceback.format_exc())
|
||||
|
||||
# First, try renaming the file.
|
||||
|
|
@ -462,13 +461,13 @@ def link(path, dest, replace=False):
|
|||
path = syspath(path)
|
||||
dest = syspath(dest)
|
||||
if os.path.exists(dest) and not replace:
|
||||
raise FilesystemError('file exists', 'rename', (path, dest),
|
||||
raise FilesystemError(u'file exists', 'rename', (path, dest),
|
||||
traceback.format_exc())
|
||||
try:
|
||||
os.symlink(path, dest)
|
||||
except OSError:
|
||||
raise FilesystemError('Operating system does not support symbolic '
|
||||
'links.', 'link', (path, dest),
|
||||
raise FilesystemError(u'Operating system does not support symbolic '
|
||||
u'links.', 'link', (path, dest),
|
||||
traceback.format_exc())
|
||||
|
||||
|
||||
|
|
@ -619,7 +618,7 @@ def legalize_path(path, replacements, length, extension, fragment):
|
|||
|
||||
def str2bool(value):
|
||||
"""Returns a boolean reflecting a human-entered string."""
|
||||
return value.lower() in ('yes', '1', 'true', 't', 'y')
|
||||
return value.lower() in (u'yes', u'1', u'true', u't', u'y')
|
||||
|
||||
|
||||
def as_string(value):
|
||||
|
|
@ -643,7 +642,7 @@ def plurality(objs):
|
|||
"""
|
||||
c = Counter(objs)
|
||||
if not c:
|
||||
raise ValueError('sequence must be non-empty')
|
||||
raise ValueError(u'sequence must be non-empty')
|
||||
return c.most_common(1)[0]
|
||||
|
||||
|
||||
|
|
@ -766,7 +765,7 @@ def shlex_split(s):
|
|||
return [c.decode('utf8') for c in shlex.split(bs)]
|
||||
|
||||
else:
|
||||
raise TypeError('shlex_split called with non-string')
|
||||
raise TypeError(u'shlex_split called with non-string')
|
||||
|
||||
|
||||
def interactive_open(targets, command):
|
||||
|
|
@ -854,8 +853,8 @@ def raw_seconds_short(string):
|
|||
Raises ValueError if the conversion cannot take place due to `string` not
|
||||
being in the right format.
|
||||
"""
|
||||
match = re.match('^(\d+):([0-5]\d)$', string)
|
||||
match = re.match(r'^(\d+):([0-5]\d)$', string)
|
||||
if not match:
|
||||
raise ValueError('String not in M:SS format')
|
||||
raise ValueError(u'String not in M:SS format')
|
||||
minutes, seconds = map(int, match.groups())
|
||||
return float(minutes * 60 + seconds)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Abstraction layer to resize images using PIL, ImageMagick, or a
|
||||
public resizing proxy if neither is available.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import urllib
|
||||
import subprocess
|
||||
|
|
@ -125,10 +124,10 @@ def im_getsize(path_in):
|
|||
try:
|
||||
out = util.command_output(cmd)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
log.warn('ImageMagick size query failed')
|
||||
log.warn(u'ImageMagick size query failed')
|
||||
log.debug(
|
||||
'`convert` exited with (status {}) when '
|
||||
'getting size with command {}:\n{}',
|
||||
u'`convert` exited with (status {}) when '
|
||||
u'getting size with command {}:\n{}',
|
||||
exc.returncode, cmd, exc.output.strip()
|
||||
)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ asyncore.
|
|||
|
||||
Bluelet: easy concurrency without all the messy parallelism.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import socket
|
||||
import select
|
||||
|
|
@ -555,7 +554,7 @@ def spawn(coro):
|
|||
and child coroutines run concurrently.
|
||||
"""
|
||||
if not isinstance(coro, types.GeneratorType):
|
||||
raise ValueError('%s is not a coroutine' % coro)
|
||||
raise ValueError(u'%s is not a coroutine' % coro)
|
||||
return SpawnEvent(coro)
|
||||
|
||||
|
||||
|
|
@ -565,7 +564,7 @@ def call(coro):
|
|||
returns a value using end(), then this event returns that value.
|
||||
"""
|
||||
if not isinstance(coro, types.GeneratorType):
|
||||
raise ValueError('%s is not a coroutine' % coro)
|
||||
raise ValueError(u'%s is not a coroutine' % coro)
|
||||
return DelegationEvent(coro)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Worry-free YAML configuration files.
|
||||
"""
|
||||
from __future__ import (unicode_literals, absolute_import, print_function,
|
||||
division)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import platform
|
||||
import os
|
||||
|
|
@ -101,17 +100,17 @@ class ConfigReadError(ConfigError):
|
|||
self.filename = filename
|
||||
self.reason = reason
|
||||
|
||||
message = 'file {0} could not be read'.format(filename)
|
||||
message = u'file {0} could not be read'.format(filename)
|
||||
if isinstance(reason, yaml.scanner.ScannerError) and \
|
||||
reason.problem == YAML_TAB_PROBLEM:
|
||||
# Special-case error message for tab indentation in YAML markup.
|
||||
message += ': found tab character at line {0}, column {1}'.format(
|
||||
message += u': found tab character at line {0}, column {1}'.format(
|
||||
reason.problem_mark.line + 1,
|
||||
reason.problem_mark.column + 1,
|
||||
)
|
||||
elif reason:
|
||||
# Generic error message uses exception's message.
|
||||
message += ': {0}'.format(reason)
|
||||
message += u': {0}'.format(reason)
|
||||
|
||||
super(ConfigReadError, self).__init__(message)
|
||||
|
||||
|
|
@ -125,15 +124,15 @@ class ConfigSource(dict):
|
|||
def __init__(self, value, filename=None, default=False):
|
||||
super(ConfigSource, self).__init__(value)
|
||||
if filename is not None and not isinstance(filename, BASESTRING):
|
||||
raise TypeError('filename must be a string or None')
|
||||
raise TypeError(u'filename must be a string or None')
|
||||
self.filename = filename
|
||||
self.default = default
|
||||
|
||||
def __repr__(self):
|
||||
return 'ConfigSource({0}, {1}, {2})'.format(
|
||||
super(ConfigSource, self).__repr__(),
|
||||
repr(self.filename),
|
||||
repr(self.default)
|
||||
return 'ConfigSource({0!r}, {1!r}, {2!r})'.format(
|
||||
super(ConfigSource, self),
|
||||
self.filename,
|
||||
self.default,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -147,7 +146,7 @@ class ConfigSource(dict):
|
|||
elif isinstance(value, dict):
|
||||
return ConfigSource(value)
|
||||
else:
|
||||
raise TypeError('source value must be a dict')
|
||||
raise TypeError(u'source value must be a dict')
|
||||
|
||||
|
||||
class ConfigView(object):
|
||||
|
|
@ -182,7 +181,7 @@ class ConfigView(object):
|
|||
try:
|
||||
return iter_first(pairs)
|
||||
except ValueError:
|
||||
raise NotFoundError("{0} not found".format(self.name))
|
||||
raise NotFoundError(u"{0} not found".format(self.name))
|
||||
|
||||
def exists(self):
|
||||
"""Determine whether the view has a setting in any source.
|
||||
|
|
@ -230,7 +229,7 @@ class ConfigView(object):
|
|||
collection = self.get()
|
||||
if not isinstance(collection, (list, tuple)):
|
||||
raise ConfigTypeError(
|
||||
'{0} must be a dictionary or a list, not {1}'.format(
|
||||
u'{0} must be a dictionary or a list, not {1}'.format(
|
||||
self.name, type(collection).__name__
|
||||
)
|
||||
)
|
||||
|
|
@ -308,7 +307,7 @@ class ConfigView(object):
|
|||
cur_keys = dic.keys()
|
||||
except AttributeError:
|
||||
raise ConfigTypeError(
|
||||
'{0} must be a dict, not {1}'.format(
|
||||
u'{0} must be a dict, not {1}'.format(
|
||||
self.name, type(dic).__name__
|
||||
)
|
||||
)
|
||||
|
|
@ -349,7 +348,7 @@ class ConfigView(object):
|
|||
it = iter(collection)
|
||||
except TypeError:
|
||||
raise ConfigTypeError(
|
||||
'{0} must be an iterable, not {1}'.format(
|
||||
u'{0} must be an iterable, not {1}'.format(
|
||||
self.name, type(collection).__name__
|
||||
)
|
||||
)
|
||||
|
|
@ -489,7 +488,7 @@ class Subview(ConfigView):
|
|||
if not isinstance(self.key, int):
|
||||
self.name += '.'
|
||||
if isinstance(self.key, int):
|
||||
self.name += '#{0}'.format(self.key)
|
||||
self.name += u'#{0}'.format(self.key)
|
||||
elif isinstance(self.key, BASESTRING):
|
||||
if isinstance(self.key, bytes):
|
||||
self.name += self.key.decode('utf8')
|
||||
|
|
@ -511,7 +510,7 @@ class Subview(ConfigView):
|
|||
except TypeError:
|
||||
# Not subscriptable.
|
||||
raise ConfigTypeError(
|
||||
"{0} must be a collection, not {1}".format(
|
||||
u"{0} must be a collection, not {1}".format(
|
||||
self.parent.name, type(collection).__name__
|
||||
)
|
||||
)
|
||||
|
|
@ -621,7 +620,7 @@ class Loader(yaml.SafeLoader):
|
|||
else:
|
||||
raise yaml.constructor.ConstructorError(
|
||||
None, None,
|
||||
'expected a mapping node, but found %s' % node.id,
|
||||
u'expected a mapping node, but found %s' % node.id,
|
||||
node.start_mark
|
||||
)
|
||||
|
||||
|
|
@ -632,7 +631,7 @@ class Loader(yaml.SafeLoader):
|
|||
hash(key)
|
||||
except TypeError as exc:
|
||||
raise yaml.constructor.ConstructorError(
|
||||
'while constructing a mapping',
|
||||
u'while constructing a mapping',
|
||||
node.start_mark, 'found unacceptable key (%s)' % exc,
|
||||
key_node.start_mark
|
||||
)
|
||||
|
|
@ -710,9 +709,9 @@ class Dumper(yaml.SafeDumper):
|
|||
"""Represent bool as 'yes' or 'no' instead of 'true' or 'false'.
|
||||
"""
|
||||
if data:
|
||||
value = 'yes'
|
||||
value = u'yes'
|
||||
else:
|
||||
value = 'no'
|
||||
value = u'no'
|
||||
return self.represent_scalar('tag:yaml.org,2002:bool', value)
|
||||
|
||||
def represent_none(self, data):
|
||||
|
|
@ -837,7 +836,7 @@ class Configuration(RootView):
|
|||
appdir = os.environ[self._env_var]
|
||||
appdir = os.path.abspath(os.path.expanduser(appdir))
|
||||
if os.path.isfile(appdir):
|
||||
raise ConfigError('{0} must be a directory'.format(
|
||||
raise ConfigError(u'{0} must be a directory'.format(
|
||||
self._env_var
|
||||
))
|
||||
|
||||
|
|
@ -990,7 +989,7 @@ class Template(object):
|
|||
return self.convert(value, view)
|
||||
elif self.default is REQUIRED:
|
||||
# Missing required value. This is an error.
|
||||
raise NotFoundError("{0} not found".format(view.name))
|
||||
raise NotFoundError(u"{0} not found".format(view.name))
|
||||
else:
|
||||
# Missing value, but not required.
|
||||
return self.default
|
||||
|
|
@ -1015,7 +1014,7 @@ class Template(object):
|
|||
"""
|
||||
exc_class = ConfigTypeError if type_error else ConfigValueError
|
||||
raise exc_class(
|
||||
'{0}: {1}'.format(view.name, message)
|
||||
u'{0}: {1}'.format(view.name, message)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
|
|
@ -1036,7 +1035,7 @@ class Integer(Template):
|
|||
elif isinstance(value, float):
|
||||
return int(value)
|
||||
else:
|
||||
self.fail('must be a number', view, True)
|
||||
self.fail(u'must be a number', view, True)
|
||||
|
||||
|
||||
class Number(Template):
|
||||
|
|
@ -1049,7 +1048,7 @@ class Number(Template):
|
|||
return value
|
||||
else:
|
||||
self.fail(
|
||||
'must be numeric, not {0}'.format(type(value).__name__),
|
||||
u'must be numeric, not {0}'.format(type(value).__name__),
|
||||
view,
|
||||
True
|
||||
)
|
||||
|
|
@ -1111,12 +1110,12 @@ class String(Template):
|
|||
if isinstance(value, BASESTRING):
|
||||
if self.pattern and not self.regex.match(value):
|
||||
self.fail(
|
||||
"must match the pattern {0}".format(self.pattern),
|
||||
u"must match the pattern {0}".format(self.pattern),
|
||||
view
|
||||
)
|
||||
return value
|
||||
else:
|
||||
self.fail('must be a string', view, True)
|
||||
self.fail(u'must be a string', view, True)
|
||||
|
||||
|
||||
class Choice(Template):
|
||||
|
|
@ -1137,7 +1136,7 @@ class Choice(Template):
|
|||
"""
|
||||
if value not in self.choices:
|
||||
self.fail(
|
||||
'must be one of {0}, not {1}'.format(
|
||||
u'must be one of {0}, not {1}'.format(
|
||||
repr(list(self.choices)), repr(value)
|
||||
),
|
||||
view
|
||||
|
|
@ -1206,7 +1205,7 @@ class OneOf(Template):
|
|||
raise ConfigTemplateError(exc)
|
||||
|
||||
self.fail(
|
||||
'must be one of {0}, not {1}'.format(
|
||||
u'must be one of {0}, not {1}'.format(
|
||||
repr(self.allowed), repr(value)
|
||||
),
|
||||
view
|
||||
|
|
@ -1242,7 +1241,7 @@ class StrSeq(Template):
|
|||
try:
|
||||
value = list(value)
|
||||
except TypeError:
|
||||
self.fail('must be a whitespace-separated string or a list',
|
||||
self.fail(u'must be a whitespace-separated string or a list',
|
||||
view, True)
|
||||
|
||||
def convert(x):
|
||||
|
|
@ -1251,7 +1250,7 @@ class StrSeq(Template):
|
|||
elif isinstance(x, bytes):
|
||||
return x.decode('utf8', 'ignore')
|
||||
else:
|
||||
self.fail('must be a list of strings', view, True)
|
||||
self.fail(u'must be a list of strings', view, True)
|
||||
return list(map(convert, value))
|
||||
|
||||
|
||||
|
|
@ -1301,19 +1300,19 @@ class Filename(Template):
|
|||
if not isinstance(template, (collections.Mapping, MappingTemplate)):
|
||||
# disallow config.get(Filename(relative_to='foo'))
|
||||
raise ConfigTemplateError(
|
||||
'relative_to may only be used when getting multiple values.'
|
||||
u'relative_to may only be used when getting multiple values.'
|
||||
)
|
||||
|
||||
elif self.relative_to == view.key:
|
||||
raise ConfigTemplateError(
|
||||
'{0} is relative to itself'.format(view.name)
|
||||
u'{0} is relative to itself'.format(view.name)
|
||||
)
|
||||
|
||||
elif self.relative_to not in view.parent.keys():
|
||||
# self.relative_to is not in the config
|
||||
self.fail(
|
||||
(
|
||||
'needs sibling value "{0}" to expand relative path'
|
||||
u'needs sibling value "{0}" to expand relative path'
|
||||
).format(self.relative_to),
|
||||
view
|
||||
)
|
||||
|
|
@ -1335,12 +1334,12 @@ class Filename(Template):
|
|||
if next_relative in template.subtemplates:
|
||||
# we encountered this config key previously
|
||||
raise ConfigTemplateError((
|
||||
'{0} and {1} are recursively relative'
|
||||
u'{0} and {1} are recursively relative'
|
||||
).format(view.name, self.relative_to))
|
||||
else:
|
||||
raise ConfigTemplateError((
|
||||
'missing template for {0}, needed to expand {1}\'s' +
|
||||
'relative path'
|
||||
u'missing template for {0}, needed to expand {1}\'s' +
|
||||
u'relative path'
|
||||
).format(self.relative_to, view.name))
|
||||
|
||||
next_template.subtemplates[next_relative] = rel_to_template
|
||||
|
|
@ -1352,7 +1351,7 @@ class Filename(Template):
|
|||
path, source = view.first()
|
||||
if not isinstance(path, BASESTRING):
|
||||
self.fail(
|
||||
'must be a filename, not {0}'.format(type(path).__name__),
|
||||
u'must be a filename, not {0}'.format(type(path).__name__),
|
||||
view,
|
||||
True
|
||||
)
|
||||
|
|
@ -1390,7 +1389,7 @@ class TypeTemplate(Template):
|
|||
def convert(self, value, view):
|
||||
if not isinstance(value, self.typ):
|
||||
self.fail(
|
||||
'must be a {0}, not {1}'.format(
|
||||
u'must be a {0}, not {1}'.format(
|
||||
self.typ.__name__,
|
||||
type(value).__name__,
|
||||
),
|
||||
|
|
@ -1444,4 +1443,4 @@ def as_template(value):
|
|||
elif isinstance(value, type):
|
||||
return TypeTemplate(value)
|
||||
else:
|
||||
raise ValueError('cannot convert to template: {0!r}'.format(value))
|
||||
raise ValueError(u'cannot convert to template: {0!r}'.format(value))
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ This is sort of like a tiny, horrible degeneration of a real templating
|
|||
engine like Jinja2 or Mustache.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
import ast
|
||||
|
|
@ -79,7 +78,7 @@ def ex_literal(val):
|
|||
return ast.Name(bytes(val), ast.Load())
|
||||
elif isinstance(val, basestring):
|
||||
return ast.Str(val)
|
||||
raise TypeError('no literal for {0}'.format(type(val)))
|
||||
raise TypeError(u'no literal for {0}'.format(type(val)))
|
||||
|
||||
|
||||
def ex_varassign(name, expr):
|
||||
|
|
@ -571,4 +570,4 @@ if __name__ == b'__main__':
|
|||
'from __main__ import _tmpl, _vars, _funcs',
|
||||
number=10000)
|
||||
print(comp_time)
|
||||
print('Speedup:', interp_time / comp_time)
|
||||
print(u'Speedup:', interp_time / comp_time)
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ To do so, pass an iterable of coroutines to the Pipeline constructor
|
|||
in place of any single coroutine.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import Queue
|
||||
from threading import Thread, Lock
|
||||
|
|
@ -362,7 +361,7 @@ class Pipeline(object):
|
|||
be at least two stages.
|
||||
"""
|
||||
if len(stages) < 2:
|
||||
raise ValueError('pipeline must have at least two stages')
|
||||
raise ValueError(u'pipeline must have at least two stages')
|
||||
self.stages = []
|
||||
for stage in stages:
|
||||
if isinstance(stage, (list, tuple)):
|
||||
|
|
@ -467,14 +466,14 @@ if __name__ == b'__main__':
|
|||
# in parallel.
|
||||
def produce():
|
||||
for i in range(5):
|
||||
print('generating %i' % i)
|
||||
print(u'generating %i' % i)
|
||||
time.sleep(1)
|
||||
yield i
|
||||
|
||||
def work():
|
||||
num = yield
|
||||
while True:
|
||||
print('processing %i' % num)
|
||||
print(u'processing %i' % num)
|
||||
time.sleep(2)
|
||||
num = yield num * 2
|
||||
|
||||
|
|
@ -482,7 +481,7 @@ if __name__ == b'__main__':
|
|||
while True:
|
||||
num = yield
|
||||
time.sleep(1)
|
||||
print('received %i' % num)
|
||||
print(u'received %i' % num)
|
||||
|
||||
ts_start = time.time()
|
||||
Pipeline([produce(), work(), consume()]).run_sequential()
|
||||
|
|
@ -491,22 +490,22 @@ if __name__ == b'__main__':
|
|||
ts_par = time.time()
|
||||
Pipeline([produce(), (work(), work()), consume()]).run_parallel()
|
||||
ts_end = time.time()
|
||||
print('Sequential time:', ts_seq - ts_start)
|
||||
print('Parallel time:', ts_par - ts_seq)
|
||||
print('Multiply-parallel time:', ts_end - ts_par)
|
||||
print(u'Sequential time:', ts_seq - ts_start)
|
||||
print(u'Parallel time:', ts_par - ts_seq)
|
||||
print(u'Multiply-parallel time:', ts_end - ts_par)
|
||||
print()
|
||||
|
||||
# Test a pipeline that raises an exception.
|
||||
def exc_produce():
|
||||
for i in range(10):
|
||||
print('generating %i' % i)
|
||||
print(u'generating %i' % i)
|
||||
time.sleep(1)
|
||||
yield i
|
||||
|
||||
def exc_work():
|
||||
num = yield
|
||||
while True:
|
||||
print('processing %i' % num)
|
||||
print(u'processing %i' % num)
|
||||
time.sleep(3)
|
||||
if num == 3:
|
||||
raise Exception()
|
||||
|
|
@ -515,6 +514,6 @@ if __name__ == b'__main__':
|
|||
def exc_consume():
|
||||
while True:
|
||||
num = yield
|
||||
print('received %i' % num)
|
||||
print(u'received %i' % num)
|
||||
|
||||
Pipeline([exc_produce(), exc_work(), exc_consume()]).run_parallel(1)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""A simple utility for constructing filesystem-like trees from beets
|
||||
libraries.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from collections import namedtuple
|
||||
from beets import util
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
"""A namespace package for beets plugins."""
|
||||
|
||||
# Make this a namespace package.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
# Make this a namespace package.
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Fetch various AcousticBrainz metadata using MBID.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import requests
|
||||
import operator
|
||||
|
|
@ -38,7 +37,7 @@ class AcousticPlugin(plugins.BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('acousticbrainz',
|
||||
help="fetch metadata from AcousticBrainz")
|
||||
help=u"fetch metadata from AcousticBrainz")
|
||||
|
||||
def func(lib, opts, args):
|
||||
items = lib.items(ui.decargs(args))
|
||||
|
|
@ -63,24 +62,24 @@ def fetch_info(log, items, write):
|
|||
try:
|
||||
return reduce(operator.getitem, map_path, data)
|
||||
except KeyError:
|
||||
log.debug('Invalid Path: {}', map_path)
|
||||
log.debug(u'Invalid Path: {}', map_path)
|
||||
|
||||
for item in items:
|
||||
if item.mb_trackid:
|
||||
log.info('getting data for: {}', item)
|
||||
log.info(u'getting data for: {}', item)
|
||||
|
||||
# Fetch the data from the AB API.
|
||||
urls = [generate_url(item.mb_trackid, path) for path in LEVELS]
|
||||
log.debug('fetching URLs: {}', ' '.join(urls))
|
||||
log.debug(u'fetching URLs: {}', ' '.join(urls))
|
||||
try:
|
||||
res = [requests.get(url) for url in urls]
|
||||
except requests.RequestException as exc:
|
||||
log.info('request error: {}', exc)
|
||||
log.info(u'request error: {}', exc)
|
||||
continue
|
||||
|
||||
# Check for missing tracks.
|
||||
if any(r.status_code == 404 for r in res):
|
||||
log.info('recording ID {} not found', item.mb_trackid)
|
||||
log.info(u'recording ID {} not found', item.mb_trackid)
|
||||
continue
|
||||
|
||||
# Parse the JSON response.
|
||||
|
|
@ -88,7 +87,7 @@ def fetch_info(log, items, write):
|
|||
data = res[0].json()
|
||||
data.update(res[1].json())
|
||||
except ValueError:
|
||||
log.debug('Invalid Response: {} & {}', [r.text for r in res])
|
||||
log.debug(u'Invalid Response: {} & {}', [r.text for r in res])
|
||||
|
||||
# Get each field and assign it on the item.
|
||||
item.danceable = get_value(
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Use command-line tools to check for audio file corruption.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand
|
||||
|
|
@ -32,7 +31,7 @@ import sys
|
|||
|
||||
class BadFiles(BeetsPlugin):
|
||||
def run_command(self, cmd):
|
||||
self._log.debug("running command: {}",
|
||||
self._log.debug(u"running command: {}",
|
||||
displayable_path(list2cmdline(cmd)))
|
||||
try:
|
||||
output = check_output(cmd, stderr=STDOUT)
|
||||
|
|
@ -44,7 +43,7 @@ class BadFiles(BeetsPlugin):
|
|||
status = e.returncode
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
ui.print_("command not found: {}".format(cmd[0]))
|
||||
ui.print_(u"command not found: {}".format(cmd[0]))
|
||||
sys.exit(1)
|
||||
else:
|
||||
raise
|
||||
|
|
@ -87,9 +86,9 @@ class BadFiles(BeetsPlugin):
|
|||
# First, check whether the path exists. If not, the user
|
||||
# should probably run `beet update` to cleanup your library.
|
||||
dpath = displayable_path(item.path)
|
||||
self._log.debug("checking path: {}", dpath)
|
||||
self._log.debug(u"checking path: {}", dpath)
|
||||
if not os.path.exists(item.path):
|
||||
ui.print_("{}: file does not exist".format(
|
||||
ui.print_(u"{}: file does not exist".format(
|
||||
ui.colorize('text_error', dpath)))
|
||||
|
||||
# Run the checker against the file if one is found
|
||||
|
|
@ -102,20 +101,20 @@ class BadFiles(BeetsPlugin):
|
|||
path = item.path.decode(sys.getfilesystemencoding())
|
||||
status, errors, output = checker(path)
|
||||
if status > 0:
|
||||
ui.print_("{}: checker exited withs status {}"
|
||||
ui.print_(u"{}: checker exited withs status {}"
|
||||
.format(ui.colorize('text_error', dpath), status))
|
||||
for line in output:
|
||||
ui.print_(" {}".format(displayable_path(line)))
|
||||
elif errors > 0:
|
||||
ui.print_("{}: checker found {} errors or warnings"
|
||||
ui.print_(u"{}: checker found {} errors or warnings"
|
||||
.format(ui.colorize('text_warning', dpath), errors))
|
||||
for line in output:
|
||||
ui.print_(" {}".format(displayable_path(line)))
|
||||
ui.print_(u" {}".format(displayable_path(line)))
|
||||
else:
|
||||
ui.print_("{}: ok".format(ui.colorize('text_success', dpath)))
|
||||
ui.print_(u"{}: ok".format(ui.colorize('text_success', dpath)))
|
||||
|
||||
def commands(self):
|
||||
bad_command = Subcommand('bad',
|
||||
help='check for corrupt or missing files')
|
||||
help=u'check for corrupt or missing files')
|
||||
bad_command.func = self.check_bad
|
||||
return [bad_command]
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ Beets library. Attempts to implement a compatible protocol to allow
|
|||
use of the wide range of MPD clients.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
from string import Template
|
||||
|
|
@ -124,9 +123,9 @@ def make_bpd_error(s_code, s_message):
|
|||
pass
|
||||
return NewBPDError
|
||||
|
||||
ArgumentTypeError = make_bpd_error(ERROR_ARG, 'invalid type for argument')
|
||||
ArgumentIndexError = make_bpd_error(ERROR_ARG, 'argument out of range')
|
||||
ArgumentNotFoundError = make_bpd_error(ERROR_NO_EXIST, 'argument not found')
|
||||
ArgumentTypeError = make_bpd_error(ERROR_ARG, u'invalid type for argument')
|
||||
ArgumentIndexError = make_bpd_error(ERROR_ARG, u'argument out of range')
|
||||
ArgumentNotFoundError = make_bpd_error(ERROR_NO_EXIST, u'argument not found')
|
||||
|
||||
|
||||
def cast_arg(t, val):
|
||||
|
|
@ -269,7 +268,7 @@ class BaseServer(object):
|
|||
conn.authenticated = True
|
||||
else:
|
||||
conn.authenticated = False
|
||||
raise BPDError(ERROR_PASSWORD, 'incorrect password')
|
||||
raise BPDError(ERROR_PASSWORD, u'incorrect password')
|
||||
|
||||
def cmd_commands(self, conn):
|
||||
"""Lists the commands available to the user."""
|
||||
|
|
@ -807,9 +806,9 @@ class Server(BaseServer):
|
|||
"""
|
||||
# Path is ignored. Also, the real MPD does this asynchronously;
|
||||
# this is done inline.
|
||||
print('Building directory tree...')
|
||||
print(u'Building directory tree...')
|
||||
self.tree = vfs.libtree(self.lib)
|
||||
print('... done.')
|
||||
print(u'... done.')
|
||||
self.updated_time = time.time()
|
||||
|
||||
# Path (directory tree) browsing.
|
||||
|
|
@ -848,7 +847,7 @@ class Server(BaseServer):
|
|||
node = self._resolve_path(path)
|
||||
if isinstance(node, int):
|
||||
# Trying to list a track.
|
||||
raise BPDError(ERROR_ARG, 'this is not a directory')
|
||||
raise BPDError(ERROR_ARG, u'this is not a directory')
|
||||
else:
|
||||
for name, itemid in iter(sorted(node.files.items())):
|
||||
item = self.lib.get_item(itemid)
|
||||
|
|
@ -1173,18 +1172,18 @@ class BPDPlugin(BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
cmd = beets.ui.Subcommand(
|
||||
'bpd', help='run an MPD-compatible music player server'
|
||||
'bpd', help=u'run an MPD-compatible music player server'
|
||||
)
|
||||
cmd.parser.add_option(
|
||||
'-d', '--debug', action='store_true',
|
||||
help='dump all MPD traffic to stdout'
|
||||
help=u'dump all MPD traffic to stdout'
|
||||
)
|
||||
|
||||
def func(lib, opts, args):
|
||||
host = args.pop(0) if args else self.config['host'].get(unicode)
|
||||
port = args.pop(0) if args else self.config['port'].get(int)
|
||||
if args:
|
||||
raise beets.ui.UserError('too many arguments')
|
||||
raise beets.ui.UserError(u'too many arguments')
|
||||
password = self.config['password'].get(unicode)
|
||||
volume = self.config['volume'].get(int)
|
||||
debug = opts.debug or False
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
music player.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
|
@ -91,7 +90,7 @@ class GstPlayer(object):
|
|||
# error
|
||||
self.player.set_state(gst.STATE_NULL)
|
||||
err, debug = message.parse_error()
|
||||
print("Error: {0}".format(err))
|
||||
print(u"Error: {0}".format(err))
|
||||
self.playing = False
|
||||
|
||||
def _set_volume(self, volume):
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Determine BPM by pressing a key to the rhythm."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import time
|
||||
|
||||
|
|
@ -59,8 +58,8 @@ class BPMPlugin(BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('bpm',
|
||||
help='determine bpm of a song by pressing \
|
||||
a key to the rhythm')
|
||||
help=u'determine bpm of a song by pressing '
|
||||
u'a key to the rhythm')
|
||||
cmd.func = self.command
|
||||
return [cmd]
|
||||
|
||||
|
|
@ -70,7 +69,7 @@ class BPMPlugin(BeetsPlugin):
|
|||
def get_bpm(self, items, write=False):
|
||||
overwrite = self.config['overwrite'].get(bool)
|
||||
if len(items) > 1:
|
||||
raise ValueError('Can only get bpm of one song at time')
|
||||
raise ValueError(u'Can only get bpm of one song at time')
|
||||
|
||||
item = items[0]
|
||||
if item['bpm']:
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Provides the %bucket{} function for path formatting.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from datetime import datetime
|
||||
import re
|
||||
|
|
@ -46,7 +45,7 @@ def span_from_str(span_str):
|
|||
"""Convert string to a 4 digits year
|
||||
"""
|
||||
if yearfrom < 100:
|
||||
raise BucketError("%d must be expressed on 4 digits" % yearfrom)
|
||||
raise BucketError(u"%d must be expressed on 4 digits" % yearfrom)
|
||||
|
||||
# if two digits only, pick closest year that ends by these two
|
||||
# digits starting from yearfrom
|
||||
|
|
@ -59,12 +58,12 @@ def span_from_str(span_str):
|
|||
|
||||
years = [int(x) for x in re.findall('\d+', span_str)]
|
||||
if not years:
|
||||
raise ui.UserError("invalid range defined for year bucket '%s': no "
|
||||
"year found" % span_str)
|
||||
raise ui.UserError(u"invalid range defined for year bucket '%s': no "
|
||||
u"year found" % span_str)
|
||||
try:
|
||||
years = [normalize_year(x, years[0]) for x in years]
|
||||
except BucketError as exc:
|
||||
raise ui.UserError("invalid range defined for year bucket '%s': %s" %
|
||||
raise ui.UserError(u"invalid range defined for year bucket '%s': %s" %
|
||||
(span_str, exc))
|
||||
|
||||
res = {'from': years[0], 'str': span_str}
|
||||
|
|
@ -119,8 +118,8 @@ def build_year_spans(year_spans_str):
|
|||
def str2fmt(s):
|
||||
"""Deduces formatting syntax from a span string.
|
||||
"""
|
||||
regex = re.compile("(?P<bef>\D*)(?P<fromyear>\d+)(?P<sep>\D*)"
|
||||
"(?P<toyear>\d*)(?P<after>\D*)")
|
||||
regex = re.compile(r"(?P<bef>\D*)(?P<fromyear>\d+)(?P<sep>\D*)"
|
||||
r"(?P<toyear>\d*)(?P<after>\D*)")
|
||||
m = re.match(regex, s)
|
||||
|
||||
res = {'fromnchars': len(m.group('fromyear')),
|
||||
|
|
@ -166,8 +165,8 @@ def build_alpha_spans(alpha_spans_str, alpha_regexs):
|
|||
beginIdx = ASCII_DIGITS.index(bucket[0])
|
||||
endIdx = ASCII_DIGITS.index(bucket[-1])
|
||||
else:
|
||||
raise ui.UserError("invalid range defined for alpha bucket "
|
||||
"'%s': no alphanumeric character found" %
|
||||
raise ui.UserError(u"invalid range defined for alpha bucket "
|
||||
u"'%s': no alphanumeric character found" %
|
||||
elem)
|
||||
spans.append(
|
||||
re.compile(
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Adds Chromaprint/Acoustid acoustic fingerprinting support to the
|
||||
autotagger. Requires the pyacoustid library.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets import plugins
|
||||
from beets import ui
|
||||
|
|
@ -178,19 +177,19 @@ class AcoustidPlugin(plugins.BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
submit_cmd = ui.Subcommand('submit',
|
||||
help='submit Acoustid fingerprints')
|
||||
help=u'submit Acoustid fingerprints')
|
||||
|
||||
def submit_cmd_func(lib, opts, args):
|
||||
try:
|
||||
apikey = config['acoustid']['apikey'].get(unicode)
|
||||
except confit.NotFoundError:
|
||||
raise ui.UserError('no Acoustid user API key provided')
|
||||
raise ui.UserError(u'no Acoustid user API key provided')
|
||||
submit_items(self._log, apikey, lib.items(ui.decargs(args)))
|
||||
submit_cmd.func = submit_cmd_func
|
||||
|
||||
fingerprint_cmd = ui.Subcommand(
|
||||
'fingerprint',
|
||||
help='generate fingerprints for items without them'
|
||||
help=u'generate fingerprints for items without them'
|
||||
)
|
||||
|
||||
def fingerprint_cmd_func(lib, opts, args):
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Converts tracks or albums to external directory
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import threading
|
||||
|
|
@ -140,21 +139,21 @@ class ConvertPlugin(BeetsPlugin):
|
|||
self.register_listener('import_task_files', self._cleanup)
|
||||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('convert', help='convert to external location')
|
||||
cmd = ui.Subcommand('convert', help=u'convert to external location')
|
||||
cmd.parser.add_option('-p', '--pretend', action='store_true',
|
||||
help='show actions but do nothing')
|
||||
help=u'show actions but do nothing')
|
||||
cmd.parser.add_option('-t', '--threads', action='store', type='int',
|
||||
help='change the number of threads, \
|
||||
help=u'change the number of threads, \
|
||||
defaults to maximum available processors')
|
||||
cmd.parser.add_option('-k', '--keep-new', action='store_true',
|
||||
dest='keep_new', help='keep only the converted \
|
||||
dest='keep_new', help=u'keep only the converted \
|
||||
and move the old files')
|
||||
cmd.parser.add_option('-d', '--dest', action='store',
|
||||
help='set the destination directory')
|
||||
help=u'set the destination directory')
|
||||
cmd.parser.add_option('-f', '--format', action='store', dest='format',
|
||||
help='set the target format of the tracks')
|
||||
help=u'set the target format of the tracks')
|
||||
cmd.parser.add_option('-y', '--yes', action='store_true', dest='yes',
|
||||
help='do not ask for confirmation')
|
||||
help=u'do not ask for confirmation')
|
||||
cmd.parser.add_album_option()
|
||||
cmd.func = self.convert_func
|
||||
return [cmd]
|
||||
|
|
@ -294,7 +293,7 @@ class ConvertPlugin(BeetsPlugin):
|
|||
if self.config['embed']:
|
||||
album = item.get_album()
|
||||
if album and album.artpath:
|
||||
self._log.debug('embedding album art from {}',
|
||||
self._log.debug(u'embedding album art from {}',
|
||||
util.displayable_path(album.artpath))
|
||||
art.embed_item(self._log, item, album.artpath,
|
||||
itempath=converted)
|
||||
|
|
@ -373,7 +372,7 @@ class ConvertPlugin(BeetsPlugin):
|
|||
if not opts.dest:
|
||||
opts.dest = self.config['dest'].get()
|
||||
if not opts.dest:
|
||||
raise ui.UserError('no convert destination set')
|
||||
raise ui.UserError(u'no convert destination set')
|
||||
opts.dest = util.bytestring_path(opts.dest)
|
||||
|
||||
if not opts.threads:
|
||||
|
|
@ -393,7 +392,7 @@ class ConvertPlugin(BeetsPlugin):
|
|||
if not pretend:
|
||||
ui.commands.list_items(lib, ui.decargs(args), opts.album)
|
||||
|
||||
if not (opts.yes or ui.input_yn("Convert? (Y/n)")):
|
||||
if not (opts.yes or ui.input_yn(u"Convert? (Y/n)")):
|
||||
return
|
||||
|
||||
if opts.album:
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Adds Discogs album search support to the autotagger. Requires the
|
||||
discogs-client library.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import beets.ui
|
||||
from beets import logging
|
||||
|
|
@ -101,24 +100,24 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
try:
|
||||
_, _, url = auth_client.get_authorize_url()
|
||||
except CONNECTION_ERRORS as e:
|
||||
self._log.debug('connection error: {0}', e)
|
||||
raise beets.ui.UserError('communication with Discogs failed')
|
||||
self._log.debug(u'connection error: {0}', e)
|
||||
raise beets.ui.UserError(u'communication with Discogs failed')
|
||||
|
||||
beets.ui.print_("To authenticate with Discogs, visit:")
|
||||
beets.ui.print_(u"To authenticate with Discogs, visit:")
|
||||
beets.ui.print_(url)
|
||||
|
||||
# Ask for the code and validate it.
|
||||
code = beets.ui.input_("Enter the code:")
|
||||
code = beets.ui.input_(u"Enter the code:")
|
||||
try:
|
||||
token, secret = auth_client.get_access_token(code)
|
||||
except DiscogsAPIError:
|
||||
raise beets.ui.UserError('Discogs authorization failed')
|
||||
raise beets.ui.UserError(u'Discogs authorization failed')
|
||||
except CONNECTION_ERRORS as e:
|
||||
self._log.debug(u'connection error: {0}', e)
|
||||
raise beets.ui.UserError('Discogs token request failed')
|
||||
raise beets.ui.UserError(u'Discogs token request failed')
|
||||
|
||||
# Save the token for later use.
|
||||
self._log.debug('Discogs token {0}, secret {1}', token, secret)
|
||||
self._log.debug(u'Discogs token {0}, secret {1}', token, secret)
|
||||
with open(self._tokenfile(), 'w') as f:
|
||||
json.dump({'token': token, 'secret': secret}, f)
|
||||
|
||||
|
|
@ -153,7 +152,7 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
else:
|
||||
return []
|
||||
except CONNECTION_ERRORS:
|
||||
self._log.debug('Connection error in album search', exc_info=True)
|
||||
self._log.debug(u'Connection error in album search', exc_info=True)
|
||||
return []
|
||||
|
||||
def album_for_id(self, album_id):
|
||||
|
|
@ -184,7 +183,7 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
return self.album_for_id(album_id)
|
||||
return None
|
||||
except CONNECTION_ERRORS:
|
||||
self._log.debug('Connection error in album lookup', exc_info=True)
|
||||
self._log.debug(u'Connection error in album lookup', exc_info=True)
|
||||
return None
|
||||
return self.get_album_info(result)
|
||||
|
||||
|
|
@ -206,7 +205,7 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
releases = self.discogs_client.search(query,
|
||||
type='release').page(1)
|
||||
except CONNECTION_ERRORS:
|
||||
self._log.debug("Communication error while searching for {0!r}",
|
||||
self._log.debug(u"Communication error while searching for {0!r}",
|
||||
query, exc_info=True)
|
||||
return []
|
||||
return [self.get_album_info(release) for release in releases[:5]]
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""List duplicate tracks or albums.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import shlex
|
||||
|
||||
|
|
@ -54,52 +53,57 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
self._command = Subcommand('duplicates',
|
||||
help=__doc__,
|
||||
aliases=['dup'])
|
||||
self._command.parser.add_option('-c', '--count', dest='count',
|
||||
action='store_true',
|
||||
help='show duplicate counts')
|
||||
|
||||
self._command.parser.add_option('-C', '--checksum', dest='checksum',
|
||||
action='store', metavar='PROG',
|
||||
help='report duplicates based on'
|
||||
' arbitrary command')
|
||||
|
||||
self._command.parser.add_option('-d', '--delete', dest='delete',
|
||||
action='store_true',
|
||||
help='delete items from library and '
|
||||
'disk')
|
||||
|
||||
self._command.parser.add_option('-F', '--full', dest='full',
|
||||
action='store_true',
|
||||
help='show all versions of duplicate'
|
||||
' tracks or albums')
|
||||
|
||||
self._command.parser.add_option('-s', '--strict', dest='strict',
|
||||
action='store_true',
|
||||
help='report duplicates only if all'
|
||||
' attributes are set')
|
||||
|
||||
self._command.parser.add_option('-k', '--keys', dest='keys',
|
||||
action='callback', metavar='KEY1 KEY2',
|
||||
callback=vararg_callback,
|
||||
help='report duplicates based on keys')
|
||||
|
||||
self._command.parser.add_option('-M', '--merge', dest='merge',
|
||||
action='store_true',
|
||||
help='merge duplicate items')
|
||||
|
||||
self._command.parser.add_option('-m', '--move', dest='move',
|
||||
action='store', metavar='DEST',
|
||||
help='move items to dest')
|
||||
|
||||
self._command.parser.add_option('-o', '--copy', dest='copy',
|
||||
action='store', metavar='DEST',
|
||||
help='copy items to dest')
|
||||
|
||||
self._command.parser.add_option('-t', '--tag', dest='tag',
|
||||
action='store',
|
||||
help='tag matched items with \'k=v\''
|
||||
' attribute')
|
||||
|
||||
self._command.parser.add_option(
|
||||
u'-c', u'--count', dest='count',
|
||||
action='store_true',
|
||||
help=u'show duplicate counts',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-C', u'--checksum', dest='checksum',
|
||||
action='store', metavar='PROG',
|
||||
help=u'report duplicates based on arbitrary command',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-d', u'--delete', dest='delete',
|
||||
action='store_true',
|
||||
help=u'delete items from library and disk',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-F', u'--full', dest='full',
|
||||
action='store_true',
|
||||
help=u'show all versions of duplicate tracks or albums',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-s', u'--strict', dest='strict',
|
||||
action='store_true',
|
||||
help=u'report duplicates only if all attributes are set',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-k', u'--keys', dest='keys',
|
||||
action='callback', metavar='KEY1 KEY2',
|
||||
callback=vararg_callback,
|
||||
help=u'report duplicates based on keys',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-M', u'--merge', dest='merge',
|
||||
action='store_true',
|
||||
help=u'merge duplicate items',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-m', u'--move', dest='move',
|
||||
action='store', metavar='DEST',
|
||||
help=u'move items to dest',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-o', u'--copy', dest='copy',
|
||||
action='store', metavar='DEST',
|
||||
help=u'copy items to dest',
|
||||
)
|
||||
self._command.parser.add_option(
|
||||
u'-t', u'--tag', dest='tag',
|
||||
action='store',
|
||||
help=u'tag matched items with \'k=v\' attribute',
|
||||
)
|
||||
self._command.parser.add_all_common_options()
|
||||
|
||||
def commands(self):
|
||||
|
|
@ -181,7 +185,9 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
try:
|
||||
k, v = tag.split('=')
|
||||
except:
|
||||
raise UserError('%s: can\'t parse k=v tag: %s' % (PLUGIN, tag))
|
||||
raise UserError(
|
||||
u"{}: can't parse k=v tag: {}".format(PLUGIN, tag)
|
||||
)
|
||||
setattr(item, k, v)
|
||||
item.store()
|
||||
|
||||
|
|
@ -195,7 +201,7 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
checksum = getattr(item, key, False)
|
||||
if not checksum:
|
||||
self._log.debug(u'key {0} on item {1} not cached:'
|
||||
'computing checksum',
|
||||
u'computing checksum',
|
||||
key, displayable_path(item.path))
|
||||
try:
|
||||
checksum = command_output(args)
|
||||
|
|
@ -208,7 +214,7 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
displayable_path(item.path), e)
|
||||
else:
|
||||
self._log.debug(u'key {0} on item {1} cached:'
|
||||
'not computing checksum',
|
||||
u'not computing checksum',
|
||||
key, displayable_path(item.path))
|
||||
return key, checksum
|
||||
|
||||
|
|
@ -225,11 +231,11 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
values = [v for v in values if v not in (None, '')]
|
||||
if strict and len(values) < len(keys):
|
||||
self._log.debug(u'some keys {0} on item {1} are null or empty:'
|
||||
' skipping',
|
||||
u' skipping',
|
||||
keys, displayable_path(obj.path))
|
||||
elif (not strict and not len(values)):
|
||||
self._log.debug(u'all keys {0} on item {1} are null or empty:'
|
||||
' skipping',
|
||||
u' skipping',
|
||||
keys, displayable_path(obj.path))
|
||||
else:
|
||||
key = tuple(values)
|
||||
|
|
@ -279,7 +285,7 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
value = getattr(o, f, None)
|
||||
if value:
|
||||
self._log.debug(u'key {0} on item {1} is null '
|
||||
'or empty: setting from item {2}',
|
||||
u'or empty: setting from item {2}',
|
||||
f, displayable_path(objs[0].path),
|
||||
displayable_path(o.path))
|
||||
setattr(objs[0], f, value)
|
||||
|
|
@ -300,7 +306,7 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
missing.album_id = objs[0].id
|
||||
missing.add(i._db)
|
||||
self._log.debug(u'item {0} missing from album {1}:'
|
||||
' merging from {2} into {3}',
|
||||
u' merging from {2} into {3}',
|
||||
missing,
|
||||
objs[0],
|
||||
displayable_path(o.path),
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Fetch a variety of acoustic metrics from The Echo Nest.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import time
|
||||
import socket
|
||||
|
|
@ -465,10 +464,11 @@ class EchonestMetadataPlugin(plugins.BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
fetch_cmd = ui.Subcommand('echonest',
|
||||
help='fetch metadata from The Echo Nest')
|
||||
help=u'fetch metadata from The Echo Nest')
|
||||
fetch_cmd.parser.add_option(
|
||||
'-f', '--force', dest='force', action='store_true', default=False,
|
||||
help='(re-)download information from the EchoNest'
|
||||
u'-f', u'--force', dest='force',
|
||||
action='store_true', default=False,
|
||||
help=u'(re-)download information from the EchoNest'
|
||||
)
|
||||
|
||||
def fetch_func(lib, opts, args):
|
||||
|
|
@ -483,10 +483,10 @@ class EchonestMetadataPlugin(plugins.BeetsPlugin):
|
|||
|
||||
fetch_cmd.func = fetch_func
|
||||
|
||||
sim_cmd = ui.Subcommand('echosim', help='show related files')
|
||||
sim_cmd = ui.Subcommand('echosim', help=u'show related files')
|
||||
sim_cmd.parser.add_option(
|
||||
'-t', '--threshold', dest='threshold', action='store',
|
||||
type='float', default=0.15, help='Set difference threshold'
|
||||
u'-t', u'--threshold', dest='threshold', action='store',
|
||||
type='float', default=0.15, help=u'Set difference threshold'
|
||||
)
|
||||
sim_cmd.parser.add_format_option()
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
|
||||
"""Open metadata information in a text editor to let the user edit it.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets import plugins
|
||||
from beets import util
|
||||
|
|
@ -70,7 +69,7 @@ def load(s):
|
|||
for d in yaml.load_all(s):
|
||||
if not isinstance(d, dict):
|
||||
raise ParseError(
|
||||
'each entry must be a dictionary; found {}'.format(
|
||||
u'each entry must be a dictionary; found {}'.format(
|
||||
type(d).__name__
|
||||
)
|
||||
)
|
||||
|
|
@ -80,7 +79,7 @@ def load(s):
|
|||
out.append({unicode(k): v for k, v in d.items()})
|
||||
|
||||
except yaml.YAMLError as e:
|
||||
raise ParseError('invalid YAML: {}'.format(e))
|
||||
raise ParseError(u'invalid YAML: {}'.format(e))
|
||||
return out
|
||||
|
||||
|
||||
|
|
@ -159,18 +158,18 @@ class EditPlugin(plugins.BeetsPlugin):
|
|||
def commands(self):
|
||||
edit_command = ui.Subcommand(
|
||||
'edit',
|
||||
help='interactively edit metadata'
|
||||
help=u'interactively edit metadata'
|
||||
)
|
||||
edit_command.parser.add_option(
|
||||
'-f', '--field',
|
||||
u'-f', u'--field',
|
||||
metavar='FIELD',
|
||||
action='append',
|
||||
help='edit this field also',
|
||||
help=u'edit this field also',
|
||||
)
|
||||
edit_command.parser.add_option(
|
||||
'--all',
|
||||
u'--all',
|
||||
action='store_true', dest='all',
|
||||
help='edit all fields',
|
||||
help=u'edit all fields',
|
||||
)
|
||||
edit_command.parser.add_album_option()
|
||||
edit_command.func = self._edit_command
|
||||
|
|
@ -184,7 +183,7 @@ class EditPlugin(plugins.BeetsPlugin):
|
|||
items, albums = _do_query(lib, query, opts.album, False)
|
||||
objs = albums if opts.album else items
|
||||
if not objs:
|
||||
ui.print_('Nothing to edit.')
|
||||
ui.print_(u'Nothing to edit.')
|
||||
return
|
||||
|
||||
# Get the fields to edit.
|
||||
|
|
@ -253,15 +252,15 @@ class EditPlugin(plugins.BeetsPlugin):
|
|||
with open(new.name) as f:
|
||||
new_str = f.read()
|
||||
if new_str == old_str:
|
||||
ui.print_("No changes; aborting.")
|
||||
ui.print_(u"No changes; aborting.")
|
||||
return False
|
||||
|
||||
# Parse the updated data.
|
||||
try:
|
||||
new_data = load(new_str)
|
||||
except ParseError as e:
|
||||
ui.print_("Could not read data: {}".format(e))
|
||||
if ui.input_yn("Edit again to fix? (Y/n)", True):
|
||||
ui.print_(u"Could not read data: {}".format(e))
|
||||
if ui.input_yn(u"Edit again to fix? (Y/n)", True):
|
||||
continue
|
||||
else:
|
||||
return False
|
||||
|
|
@ -276,18 +275,18 @@ class EditPlugin(plugins.BeetsPlugin):
|
|||
for obj, obj_old in zip(objs, objs_old):
|
||||
changed |= ui.show_model_changes(obj, obj_old)
|
||||
if not changed:
|
||||
ui.print_('No changes to apply.')
|
||||
ui.print_(u'No changes to apply.')
|
||||
return False
|
||||
|
||||
# Confirm the changes.
|
||||
choice = ui.input_options(
|
||||
('continue Editing', 'apply', 'cancel')
|
||||
(u'continue Editing', u'apply', u'cancel')
|
||||
)
|
||||
if choice == 'a': # Apply.
|
||||
if choice == u'a': # Apply.
|
||||
return True
|
||||
elif choice == 'c': # Cancel.
|
||||
elif choice == u'c': # Cancel.
|
||||
return False
|
||||
elif choice == 'e': # Keep editing.
|
||||
elif choice == u'e': # Keep editing.
|
||||
# Reset the temporary changes to the objects.
|
||||
for obj in objs:
|
||||
obj.read()
|
||||
|
|
@ -305,7 +304,7 @@ class EditPlugin(plugins.BeetsPlugin):
|
|||
are temporary.
|
||||
"""
|
||||
if len(old_data) != len(new_data):
|
||||
self._log.warn('number of objects changed from {} to {}',
|
||||
self._log.warn(u'number of objects changed from {} to {}',
|
||||
len(old_data), len(new_data))
|
||||
|
||||
obj_by_id = {o.id: o for o in objs}
|
||||
|
|
@ -316,7 +315,7 @@ class EditPlugin(plugins.BeetsPlugin):
|
|||
forbidden = False
|
||||
for key in ignore_fields:
|
||||
if old_dict.get(key) != new_dict.get(key):
|
||||
self._log.warn('ignoring object whose {} changed', key)
|
||||
self._log.warn(u'ignoring object whose {} changed', key)
|
||||
forbidden = True
|
||||
break
|
||||
if forbidden:
|
||||
|
|
@ -331,7 +330,7 @@ class EditPlugin(plugins.BeetsPlugin):
|
|||
# Save to the database and possibly write tags.
|
||||
for ob in objs:
|
||||
if ob._dirty:
|
||||
self._log.debug('saving changes to {}', ob)
|
||||
self._log.debug(u'saving changes to {}', ob)
|
||||
ob.try_sync(ui.should_write(), ui.should_move())
|
||||
|
||||
# Methods for interactive importer execution.
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""Allows beets to embed album art into file metadata."""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os.path
|
||||
|
||||
|
|
@ -56,10 +55,10 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
|||
def commands(self):
|
||||
# Embed command.
|
||||
embed_cmd = ui.Subcommand(
|
||||
'embedart', help='embed image files into file metadata'
|
||||
'embedart', help=u'embed image files into file metadata'
|
||||
)
|
||||
embed_cmd.parser.add_option(
|
||||
'-f', '--file', metavar='PATH', help='the image file to embed'
|
||||
u'-f', u'--file', metavar='PATH', help=u'the image file to embed'
|
||||
)
|
||||
maxwidth = self.config['maxwidth'].get(int)
|
||||
compare_threshold = self.config['compare_threshold'].get(int)
|
||||
|
|
@ -84,17 +83,22 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
|||
embed_cmd.func = embed_func
|
||||
|
||||
# Extract command.
|
||||
extract_cmd = ui.Subcommand('extractart',
|
||||
help='extract an image from file metadata')
|
||||
extract_cmd.parser.add_option('-o', dest='outpath',
|
||||
help='image output file')
|
||||
extract_cmd.parser.add_option('-n', dest='filename',
|
||||
help='image filename to create for all '
|
||||
'matched albums')
|
||||
extract_cmd.parser.add_option('-a', dest='associate',
|
||||
action='store_true',
|
||||
help='associate the extracted images '
|
||||
'with the album')
|
||||
extract_cmd = ui.Subcommand(
|
||||
'extractart',
|
||||
help=u'extract an image from file metadata',
|
||||
)
|
||||
extract_cmd.parser.add_option(
|
||||
u'-o', dest='outpath',
|
||||
help=u'image output file',
|
||||
)
|
||||
extract_cmd.parser.add_option(
|
||||
u'-n', dest='filename',
|
||||
help=u'image filename to create for all matched albums',
|
||||
)
|
||||
extract_cmd.parser.add_option(
|
||||
'-a', dest='associate', action='store_true',
|
||||
help='associate the extracted images with the album',
|
||||
)
|
||||
|
||||
def extract_func(lib, opts, args):
|
||||
if opts.outpath:
|
||||
|
|
@ -104,8 +108,8 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
|||
filename = bytestring_path(opts.filename or
|
||||
config['art_filename'].get())
|
||||
if os.path.dirname(filename) != '':
|
||||
self._log.error(u"Only specify a name rather than a path "
|
||||
u"for -n")
|
||||
self._log.error(
|
||||
u"Only specify a name rather than a path for -n")
|
||||
return
|
||||
for album in lib.albums(decargs(args)):
|
||||
artpath = normpath(os.path.join(album.path, filename))
|
||||
|
|
@ -117,8 +121,10 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
|||
extract_cmd.func = extract_func
|
||||
|
||||
# Clear command.
|
||||
clear_cmd = ui.Subcommand('clearart',
|
||||
help='remove images from file metadata')
|
||||
clear_cmd = ui.Subcommand(
|
||||
'clearart',
|
||||
help=u'remove images from file metadata',
|
||||
)
|
||||
|
||||
def clear_func(lib, opts, args):
|
||||
art.clear(self._log, lib, decargs(args))
|
||||
|
|
@ -142,7 +148,7 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
|||
"""
|
||||
if self.config['remove_art_file'] and album.artpath:
|
||||
if os.path.isfile(album.artpath):
|
||||
self._log.debug('Removing album art file for {0}', album)
|
||||
self._log.debug(u'Removing album art file for {0}', album)
|
||||
os.remove(album.artpath)
|
||||
album.artpath = None
|
||||
album.store()
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@
|
|||
username: user
|
||||
password: password
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets import config
|
||||
from beets.plugins import BeetsPlugin
|
||||
|
|
@ -120,7 +119,8 @@ class EmbyUpdate(BeetsPlugin):
|
|||
token = get_token(host, port, headers, auth_data)
|
||||
if not token:
|
||||
self._log.warning(
|
||||
u'Couldnt not get token for user {0}'.format(username))
|
||||
u'Could not get token for user {0}', username
|
||||
)
|
||||
return
|
||||
|
||||
# Recreate headers with a token.
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Fetches album art.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from contextlib import closing
|
||||
import os
|
||||
|
|
@ -206,14 +205,14 @@ class ITunesStore(ArtSource):
|
|||
try:
|
||||
results = itunes.search_album(search_string)
|
||||
except Exception as exc:
|
||||
self._log.debug('iTunes search failed: {0}', exc)
|
||||
self._log.debug(u'iTunes search failed: {0}', exc)
|
||||
return
|
||||
|
||||
# Get the first match.
|
||||
if results:
|
||||
itunes_album = results[0]
|
||||
else:
|
||||
self._log.debug('iTunes search for {:r} got no results',
|
||||
self._log.debug(u'iTunes search for {:r} got no results',
|
||||
search_string)
|
||||
return
|
||||
|
||||
|
|
@ -276,9 +275,9 @@ class Wikipedia(ArtSource):
|
|||
cover_filename = 'File:' + results[0]['coverFilename']['value']
|
||||
page_id = results[0]['pageId']['value']
|
||||
else:
|
||||
self._log.debug('wikipedia: album not found on dbpedia')
|
||||
self._log.debug(u'wikipedia: album not found on dbpedia')
|
||||
except (ValueError, KeyError, IndexError):
|
||||
self._log.debug('wikipedia: error scraping dbpedia response: {}',
|
||||
self._log.debug(u'wikipedia: error scraping dbpedia response: {}',
|
||||
dbpedia_response.text)
|
||||
|
||||
# Ensure we have a filename before attempting to query wikipedia
|
||||
|
|
@ -293,7 +292,7 @@ class Wikipedia(ArtSource):
|
|||
if ' .' in cover_filename and \
|
||||
'.' not in cover_filename.split(' .')[-1]:
|
||||
self._log.debug(
|
||||
'wikipedia: dbpedia provided incomplete cover_filename'
|
||||
u'wikipedia: dbpedia provided incomplete cover_filename'
|
||||
)
|
||||
lpart, rpart = cover_filename.rsplit(' .', 1)
|
||||
|
||||
|
|
@ -322,7 +321,7 @@ class Wikipedia(ArtSource):
|
|||
break
|
||||
except (ValueError, KeyError):
|
||||
self._log.debug(
|
||||
'wikipedia: failed to retrieve a cover_filename'
|
||||
u'wikipedia: failed to retrieve a cover_filename'
|
||||
)
|
||||
return
|
||||
|
||||
|
|
@ -347,7 +346,7 @@ class Wikipedia(ArtSource):
|
|||
image_url = result['imageinfo'][0]['url']
|
||||
yield image_url
|
||||
except (ValueError, KeyError, IndexError):
|
||||
self._log.debug('wikipedia: error scraping imageinfo')
|
||||
self._log.debug(u'wikipedia: error scraping imageinfo')
|
||||
return
|
||||
|
||||
|
||||
|
|
@ -493,9 +492,11 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
# Manual album art fetching.
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('fetchart', help='download album art')
|
||||
cmd.parser.add_option('-f', '--force', dest='force',
|
||||
action='store_true', default=False,
|
||||
help='re-download art when already present')
|
||||
cmd.parser.add_option(
|
||||
u'-f', u'--force', dest='force',
|
||||
action='store_true', default=False,
|
||||
help=u're-download art when already present'
|
||||
)
|
||||
|
||||
def func(lib, opts, args):
|
||||
self.batch_fetch_art(lib, lib.albums(ui.decargs(args)), opts.force)
|
||||
|
|
@ -511,12 +512,12 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
"""
|
||||
try:
|
||||
with closing(self.request(url, stream=True,
|
||||
message='downloading image')) as resp:
|
||||
message=u'downloading image')) as resp:
|
||||
if 'Content-Type' not in resp.headers \
|
||||
or resp.headers['Content-Type'] not in CONTENT_TYPES:
|
||||
self._log.debug(
|
||||
'not a supported image: {}',
|
||||
resp.headers.get('Content-Type') or 'no content type',
|
||||
u'not a supported image: {}',
|
||||
resp.headers.get('Content-Type') or u'no content type',
|
||||
)
|
||||
return None
|
||||
|
||||
|
|
@ -532,7 +533,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
except (IOError, requests.RequestException, TypeError) as exc:
|
||||
# Handling TypeError works around a urllib3 bug:
|
||||
# https://github.com/shazow/urllib3/issues/556
|
||||
self._log.debug('error fetching art: {}', exc)
|
||||
self._log.debug(u'error fetching art: {}', exc)
|
||||
return None
|
||||
|
||||
def _is_valid_image_candidate(self, candidate):
|
||||
|
|
@ -551,7 +552,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
|
||||
# get_size returns None if no local imaging backend is available
|
||||
size = ArtResizer.shared.get_size(candidate)
|
||||
self._log.debug('image size: {}', size)
|
||||
self._log.debug(u'image size: {}', size)
|
||||
|
||||
if not size:
|
||||
self._log.warning(u'Could not get size of image (please see '
|
||||
|
|
@ -562,19 +563,19 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
|
||||
# Check minimum size.
|
||||
if self.minwidth and size[0] < self.minwidth:
|
||||
self._log.debug('image too small ({} < {})',
|
||||
self._log.debug(u'image too small ({} < {})',
|
||||
size[0], self.minwidth)
|
||||
return CANDIDATE_BAD
|
||||
|
||||
# Check aspect ratio.
|
||||
if self.enforce_ratio and size[0] != size[1]:
|
||||
self._log.debug('image is not square ({} != {})',
|
||||
self._log.debug(u'image is not square ({} != {})',
|
||||
size[0], size[1])
|
||||
return CANDIDATE_BAD
|
||||
|
||||
# Check maximum size.
|
||||
if self.maxwidth and size[0] > self.maxwidth:
|
||||
self._log.debug('image needs resizing ({} > {})',
|
||||
self._log.debug(u'image needs resizing ({} > {})',
|
||||
size[0], self.maxwidth)
|
||||
return CANDIDATE_DOWNSCALE
|
||||
|
||||
|
|
@ -600,7 +601,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
check = self._is_valid_image_candidate(candidate)
|
||||
if check:
|
||||
out = candidate
|
||||
self._log.debug('found local image {}', out)
|
||||
self._log.debug(u'found local image {}', out)
|
||||
break
|
||||
|
||||
# Web art sources.
|
||||
|
|
@ -613,7 +614,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
check = self._is_valid_image_candidate(candidate)
|
||||
if check:
|
||||
out = candidate
|
||||
self._log.debug('using remote image {}', out)
|
||||
self._log.debug(u'using remote image {}', out)
|
||||
break
|
||||
|
||||
if self.maxwidth and out and check == CANDIDATE_DOWNSCALE:
|
||||
|
|
@ -627,7 +628,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
"""
|
||||
for album in albums:
|
||||
if album.artpath and not force and os.path.isfile(album.artpath):
|
||||
message = ui.colorize('text_highlight_minor', 'has album art')
|
||||
message = ui.colorize('text_highlight_minor', u'has album art')
|
||||
else:
|
||||
# In ordinary invocations, look for images on the
|
||||
# filesystem. When forcing, however, always go to the Web
|
||||
|
|
@ -638,9 +639,9 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
if path:
|
||||
album.set_art(path, False)
|
||||
album.store()
|
||||
message = ui.colorize('text_success', 'found album art')
|
||||
message = ui.colorize('text_success', u'found album art')
|
||||
else:
|
||||
message = ui.colorize('text_error', 'no art found')
|
||||
message = ui.colorize('text_error', u'no art found')
|
||||
|
||||
self._log.info(u'{0}: {1}', album, message)
|
||||
|
||||
|
|
@ -654,7 +655,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
|
|||
source_names = {v: k for k, v in ART_SOURCES.items()}
|
||||
for source in self.sources:
|
||||
self._log.debug(
|
||||
'trying source {0} for album {1.albumartist} - {1.album}',
|
||||
u'trying source {0} for album {1.albumartist} - {1.album}',
|
||||
source_names[type(source)],
|
||||
album,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Creates freedesktop.org-compliant .directory files on an album level.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
|
|
@ -25,13 +24,14 @@ from beets import ui
|
|||
|
||||
class FreedesktopPlugin(BeetsPlugin):
|
||||
def commands(self):
|
||||
deprecated = ui.Subcommand("freedesktop", help="Print a message to "
|
||||
"redirect to thumbnails --dolphin")
|
||||
deprecated = ui.Subcommand(
|
||||
"freedesktop",
|
||||
help=u"Print a message to redirect to thumbnails --dolphin")
|
||||
deprecated.func = self.deprecation_message
|
||||
return [deprecated]
|
||||
|
||||
def deprecation_message(self, lib, opts, args):
|
||||
ui.print_("This plugin is deprecated. Its functionality is superseded "
|
||||
"by the 'thumbnails' plugin")
|
||||
ui.print_("'thumbnails --dolphin' replaces freedesktop. See doc & "
|
||||
"changelog for more information")
|
||||
ui.print_(u"This plugin is deprecated. Its functionality is "
|
||||
u"superseded by the 'thumbnails' plugin")
|
||||
ui.print_(u"'thumbnails --dolphin' replaces freedesktop. See doc & "
|
||||
u"changelog for more information")
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""If the title is empty, try to extract track and title from the
|
||||
filename.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets import plugins
|
||||
from beets.util import displayable_path
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Moves "featured" artists to the title from the artist field.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
|
||||
|
|
@ -87,12 +86,12 @@ class FtInTitlePlugin(plugins.BeetsPlugin):
|
|||
|
||||
self._command = ui.Subcommand(
|
||||
'ftintitle',
|
||||
help='move featured artists to the title field')
|
||||
help=u'move featured artists to the title field')
|
||||
|
||||
self._command.parser.add_option(
|
||||
'-d', '--drop', dest='drop',
|
||||
u'-d', u'--drop', dest='drop',
|
||||
action='store_true', default=False,
|
||||
help='drop featuring from artists and ignore title update')
|
||||
help=u'drop featuring from artists and ignore title update')
|
||||
|
||||
if self.config['auto']:
|
||||
self.import_stages = [self.imported]
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Provides a fuzzy matching query.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.dbcore.query import StringFieldQuery
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
"""Warns you about things you hate (or even blocks import)."""
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ modification time (mtime) of the item's source file before import.
|
|||
|
||||
Reimported albums and items are skipped.
|
||||
"""
|
||||
from __future__ import (unicode_literals, absolute_import, print_function,
|
||||
division)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
"""Write paths of imported files in various formats to ease later import in a
|
||||
music player. Also allow printing the new file locations to stdout in case
|
||||
|
|
@ -135,9 +134,9 @@ class ImportFeedsPlugin(BeetsPlugin):
|
|||
os.symlink(syspath(path), syspath(dest))
|
||||
|
||||
if 'echo' in formats:
|
||||
self._log.info("Location of imported music:")
|
||||
self._log.info(u"Location of imported music:")
|
||||
for path in paths:
|
||||
self._log.info(" {0}", path)
|
||||
self._log.info(u" {0}", path)
|
||||
|
||||
def library_opened(self, lib):
|
||||
if self.config['dir'].get() is None:
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Shows file metadata.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import re
|
||||
|
|
@ -141,17 +140,25 @@ def print_data_keys(data, item=None):
|
|||
class InfoPlugin(BeetsPlugin):
|
||||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('info', help='show file metadata')
|
||||
cmd = ui.Subcommand('info', help=u'show file metadata')
|
||||
cmd.func = self.run
|
||||
cmd.parser.add_option('-l', '--library', action='store_true',
|
||||
help='show library fields instead of tags')
|
||||
cmd.parser.add_option('-s', '--summarize', action='store_true',
|
||||
help='summarize the tags of all files')
|
||||
cmd.parser.add_option('-i', '--include-keys', default=[],
|
||||
action='append', dest='included_keys',
|
||||
help='comma separated list of keys to show')
|
||||
cmd.parser.add_option('-k', '--keys-only', action='store_true',
|
||||
help='show only the keys')
|
||||
cmd.parser.add_option(
|
||||
u'-l', u'--library', action='store_true',
|
||||
help=u'show library fields instead of tags',
|
||||
)
|
||||
cmd.parser.add_option(
|
||||
u'-s', u'--summarize', action='store_true',
|
||||
help=u'summarize the tags of all files',
|
||||
)
|
||||
cmd.parser.add_option(
|
||||
u'-i', u'--include-keys', default=[],
|
||||
action='append', dest='included_keys',
|
||||
help=u'comma separated list of keys to show',
|
||||
)
|
||||
cmd.parser.add_option(
|
||||
u'-k', u'--keys-only', action='store_true',
|
||||
help=u'show only the keys',
|
||||
)
|
||||
cmd.parser.add_format_option(target='item')
|
||||
return [cmd]
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Allows inline path template customization code in the config file.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import traceback
|
||||
import itertools
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Uses the `KeyFinder` program to add the `initial_key` field.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import subprocess
|
||||
|
||||
|
|
@ -41,7 +40,7 @@ class KeyFinderPlugin(BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('keyfinder',
|
||||
help='detect and add initial key from audio')
|
||||
help=u'detect and add initial key from audio')
|
||||
cmd.func = self.command
|
||||
return [cmd]
|
||||
|
||||
|
|
@ -63,12 +62,12 @@ class KeyFinderPlugin(BeetsPlugin):
|
|||
output = util.command_output([bin, b'-f',
|
||||
util.syspath(item.path)])
|
||||
except (subprocess.CalledProcessError, OSError) as exc:
|
||||
self._log.error('execution failed: {0}', exc)
|
||||
self._log.error(u'execution failed: {0}', exc)
|
||||
continue
|
||||
except UnicodeEncodeError:
|
||||
# Workaround for Python 2 Windows bug.
|
||||
# http://bugs.python.org/issue1759845
|
||||
self._log.error('execution failed for Unicode path: {0!r}',
|
||||
self._log.error(u'execution failed for Unicode path: {0!r}',
|
||||
item.path)
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
"""Gets genres for imported music based on Last.fm tags.
|
||||
|
||||
|
|
@ -239,25 +238,30 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
def fetch_album_genre(self, obj):
|
||||
"""Return the album genre for this Item or Album.
|
||||
"""
|
||||
return self._last_lookup(u'album', LASTFM.get_album, obj.albumartist,
|
||||
obj.album)
|
||||
return self._last_lookup(
|
||||
u'album', LASTFM.get_album, obj.albumartist, obj.album
|
||||
)
|
||||
|
||||
def fetch_album_artist_genre(self, obj):
|
||||
"""Return the album artist genre for this Item or Album.
|
||||
"""
|
||||
return self._last_lookup(u'artist', LASTFM.get_artist,
|
||||
obj.albumartist)
|
||||
return self._last_lookup(
|
||||
u'artist', LASTFM.get_artist, obj.albumartist
|
||||
)
|
||||
|
||||
def fetch_artist_genre(self, item):
|
||||
"""Returns the track artist genre for this Item.
|
||||
"""
|
||||
return self._last_lookup(u'artist', LASTFM.get_artist, item.artist)
|
||||
return self._last_lookup(
|
||||
u'artist', LASTFM.get_artist, item.artist
|
||||
)
|
||||
|
||||
def fetch_track_genre(self, obj):
|
||||
"""Returns the track genre for this Item.
|
||||
"""
|
||||
return self._last_lookup(u'track', LASTFM.get_track, obj.artist,
|
||||
obj.title)
|
||||
return self._last_lookup(
|
||||
u'track', LASTFM.get_track, obj.artist, obj.title
|
||||
)
|
||||
|
||||
def _get_genre(self, obj):
|
||||
"""Get the genre string for an Album or Item object based on
|
||||
|
|
@ -326,14 +330,15 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
return None, None
|
||||
|
||||
def commands(self):
|
||||
lastgenre_cmd = ui.Subcommand('lastgenre', help='fetch genres')
|
||||
lastgenre_cmd = ui.Subcommand('lastgenre', help=u'fetch genres')
|
||||
lastgenre_cmd.parser.add_option(
|
||||
'-f', '--force', dest='force', action='store_true', default=False,
|
||||
help='re-download genre when already present'
|
||||
u'-f', u'--force', dest='force',
|
||||
action='store_true', default=False,
|
||||
help=u're-download genre when already present'
|
||||
)
|
||||
lastgenre_cmd.parser.add_option(
|
||||
'-s', '--source', dest='source', type='string',
|
||||
help='genre source: artist, album, or track'
|
||||
u'-s', u'--source', dest='source', type='string',
|
||||
help=u'genre source: artist, album, or track'
|
||||
)
|
||||
|
||||
def lastgenre_func(lib, opts, args):
|
||||
|
|
@ -406,8 +411,8 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
return []
|
||||
except Exception as exc:
|
||||
# Isolate bugs in pylast.
|
||||
self._log.debug('{}', traceback.format_exc())
|
||||
self._log.error('error in pylast library: {0}', exc)
|
||||
self._log.debug(u'{}', traceback.format_exc())
|
||||
self._log.error(u'error in pylast library: {0}', exc)
|
||||
return []
|
||||
|
||||
# Filter by weight (optionally).
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import pylast
|
||||
from pylast import TopItem, _extract, _number
|
||||
|
|
@ -44,7 +43,7 @@ class LastImportPlugin(plugins.BeetsPlugin):
|
|||
}
|
||||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('lastimport', help='import last.fm play-count')
|
||||
cmd = ui.Subcommand('lastimport', help=u'import last.fm play-count')
|
||||
|
||||
def func(lib, opts, args):
|
||||
import_lastfm(lib, self._log)
|
||||
|
|
@ -115,9 +114,9 @@ def import_lastfm(lib, log):
|
|||
per_page = config['lastimport']['per_page'].get(int)
|
||||
|
||||
if not user:
|
||||
raise ui.UserError('You must specify a user name for lastimport')
|
||||
raise ui.UserError(u'You must specify a user name for lastimport')
|
||||
|
||||
log.info('Fetching last.fm library for @{0}', user)
|
||||
log.info(u'Fetching last.fm library for @{0}', user)
|
||||
|
||||
page_total = 1
|
||||
page_current = 0
|
||||
|
|
@ -126,7 +125,7 @@ def import_lastfm(lib, log):
|
|||
retry_limit = config['lastimport']['retry_limit'].get(int)
|
||||
# Iterate through a yet to be known page total count
|
||||
while page_current < page_total:
|
||||
log.info('Querying page #{0}{1}...',
|
||||
log.info(u'Querying page #{0}{1}...',
|
||||
page_current + 1,
|
||||
'/{}'.format(page_total) if page_total > 1 else '')
|
||||
|
||||
|
|
@ -134,7 +133,7 @@ def import_lastfm(lib, log):
|
|||
tracks, page_total = fetch_tracks(user, page_current + 1, per_page)
|
||||
if page_total < 1:
|
||||
# It means nothing to us!
|
||||
raise ui.UserError('Last.fm reported no data.')
|
||||
raise ui.UserError(u'Last.fm reported no data.')
|
||||
|
||||
if tracks:
|
||||
found, unknown = process_tracks(lib, tracks, log)
|
||||
|
|
@ -142,22 +141,22 @@ def import_lastfm(lib, log):
|
|||
unknown_total += unknown
|
||||
break
|
||||
else:
|
||||
log.error('ERROR: unable to read page #{0}',
|
||||
log.error(u'ERROR: unable to read page #{0}',
|
||||
page_current + 1)
|
||||
if retry < retry_limit:
|
||||
log.info(
|
||||
'Retrying page #{0}... ({1}/{2} retry)',
|
||||
u'Retrying page #{0}... ({1}/{2} retry)',
|
||||
page_current + 1, retry + 1, retry_limit
|
||||
)
|
||||
else:
|
||||
log.error('FAIL: unable to fetch page #{0}, ',
|
||||
'tried {1} times', page_current, retry + 1)
|
||||
log.error(u'FAIL: unable to fetch page #{0}, ',
|
||||
u'tried {1} times', page_current, retry + 1)
|
||||
page_current += 1
|
||||
|
||||
log.info('... done!')
|
||||
log.info('finished processing {0} song pages', page_total)
|
||||
log.info('{0} unknown play-counts', unknown_total)
|
||||
log.info('{0} play-counts imported', found_total)
|
||||
log.info(u'... done!')
|
||||
log.info(u'finished processing {0} song pages', page_total)
|
||||
log.info(u'{0} unknown play-counts', unknown_total)
|
||||
log.info(u'{0} play-counts imported', found_total)
|
||||
|
||||
|
||||
def fetch_tracks(user, page, limit):
|
||||
|
|
@ -191,7 +190,7 @@ def process_tracks(lib, tracks, log):
|
|||
total = len(tracks)
|
||||
total_found = 0
|
||||
total_fails = 0
|
||||
log.info('Received {0} tracks in this page, processing...', total)
|
||||
log.info(u'Received {0} tracks in this page, processing...', total)
|
||||
|
||||
for num in xrange(0, total):
|
||||
song = None
|
||||
|
|
@ -244,7 +243,7 @@ def process_tracks(lib, tracks, log):
|
|||
artist, title, album)
|
||||
|
||||
if total_fails > 0:
|
||||
log.info('Acquired {0}/{1} play-counts ({2} unknown)',
|
||||
log.info(u'Acquired {0}/{1} play-counts ({2} unknown)',
|
||||
total_found, total, total_fails)
|
||||
|
||||
return total_found, total_fails
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Fetches, embeds, and displays lyrics.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
import requests
|
||||
|
|
@ -107,7 +106,7 @@ def extract_text_in(html, starttag):
|
|||
parts.append(html[pos:match.start()])
|
||||
break
|
||||
else:
|
||||
print('no closing tag found!')
|
||||
print(u'no closing tag found!')
|
||||
return
|
||||
return u''.join(parts)
|
||||
|
||||
|
|
@ -237,7 +236,7 @@ class Genius(Backend):
|
|||
url = u'https://api.genius.com/search?q=%s' \
|
||||
% (urllib.quote(query.encode('utf8')))
|
||||
|
||||
self._log.debug('genius: requesting search {}', url)
|
||||
self._log.debug(u'genius: requesting search {}', url)
|
||||
try:
|
||||
req = requests.get(
|
||||
url,
|
||||
|
|
@ -246,19 +245,19 @@ class Genius(Backend):
|
|||
)
|
||||
req.raise_for_status()
|
||||
except requests.RequestException as exc:
|
||||
self._log.debug('genius: request error: {}', exc)
|
||||
self._log.debug(u'genius: request error: {}', exc)
|
||||
return None
|
||||
|
||||
try:
|
||||
return req.json()
|
||||
except ValueError:
|
||||
self._log.debug('genius: invalid response: {}', req.text)
|
||||
self._log.debug(u'genius: invalid response: {}', req.text)
|
||||
return None
|
||||
|
||||
def get_lyrics(self, link):
|
||||
url = u'http://genius-api.com/api/lyricsInfo'
|
||||
|
||||
self._log.debug('genius: requesting lyrics for link {}', link)
|
||||
self._log.debug(u'genius: requesting lyrics for link {}', link)
|
||||
try:
|
||||
req = requests.post(
|
||||
url,
|
||||
|
|
@ -268,13 +267,13 @@ class Genius(Backend):
|
|||
)
|
||||
req.raise_for_status()
|
||||
except requests.RequestException as exc:
|
||||
self._log.debug('genius: request error: {}', exc)
|
||||
self._log.debug(u'genius: request error: {}', exc)
|
||||
return None
|
||||
|
||||
try:
|
||||
return req.json()
|
||||
except ValueError:
|
||||
self._log.debug('genius: invalid response: {}', req.text)
|
||||
self._log.debug(u'genius: invalid response: {}', req.text)
|
||||
return None
|
||||
|
||||
def build_lyric_string(self, lyrics):
|
||||
|
|
@ -576,12 +575,16 @@ class LyricsPlugin(plugins.BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('lyrics', help='fetch song lyrics')
|
||||
cmd.parser.add_option('-p', '--print', dest='printlyr',
|
||||
action='store_true', default=False,
|
||||
help='print lyrics to console')
|
||||
cmd.parser.add_option('-f', '--force', dest='force_refetch',
|
||||
action='store_true', default=False,
|
||||
help='always re-download lyrics')
|
||||
cmd.parser.add_option(
|
||||
u'-p', u'--print', dest='printlyr',
|
||||
action='store_true', default=False,
|
||||
help=u'print lyrics to console',
|
||||
)
|
||||
cmd.parser.add_option(
|
||||
u'-f', u'--force', dest='force_refetch',
|
||||
action='store_true', default=False,
|
||||
help=u'always re-download lyrics',
|
||||
)
|
||||
|
||||
def func(lib, opts, args):
|
||||
# The "write to files" option corresponds to the
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand
|
||||
|
|
@ -34,11 +33,11 @@ def mb_call(func, *args, **kwargs):
|
|||
try:
|
||||
return func(*args, **kwargs)
|
||||
except musicbrainzngs.AuthenticationError:
|
||||
raise ui.UserError('authentication with MusicBrainz failed')
|
||||
raise ui.UserError(u'authentication with MusicBrainz failed')
|
||||
except (musicbrainzngs.ResponseError, musicbrainzngs.NetworkError) as exc:
|
||||
raise ui.UserError('MusicBrainz API error: {0}'.format(exc))
|
||||
raise ui.UserError(u'MusicBrainz API error: {0}'.format(exc))
|
||||
except musicbrainzngs.UsageError:
|
||||
raise ui.UserError('MusicBrainz credentials missing')
|
||||
raise ui.UserError(u'MusicBrainz credentials missing')
|
||||
|
||||
|
||||
def submit_albums(collection_id, release_ids):
|
||||
|
|
@ -65,7 +64,8 @@ class MusicBrainzCollectionPlugin(BeetsPlugin):
|
|||
self.import_stages = [self.imported]
|
||||
|
||||
def commands(self):
|
||||
mbupdate = Subcommand('mbupdate', help='Update MusicBrainz collection')
|
||||
mbupdate = Subcommand('mbupdate',
|
||||
help=u'Update MusicBrainz collection')
|
||||
mbupdate.func = self.update_collection
|
||||
return [mbupdate]
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ class MusicBrainzCollectionPlugin(BeetsPlugin):
|
|||
# Get the available collections.
|
||||
collections = mb_call(musicbrainzngs.get_collections)
|
||||
if not collections['collection-list']:
|
||||
raise ui.UserError('no collections exist for user')
|
||||
raise ui.UserError(u'no collections exist for user')
|
||||
|
||||
# Get the first release collection. MusicBrainz also has event
|
||||
# collections, so we need to avoid adding to those.
|
||||
|
|
@ -93,7 +93,7 @@ class MusicBrainzCollectionPlugin(BeetsPlugin):
|
|||
collection_id = collection['id']
|
||||
break
|
||||
else:
|
||||
raise ui.UserError('No collection found.')
|
||||
raise ui.UserError(u'No collection found.')
|
||||
|
||||
# Get a list of all the album IDs.
|
||||
album_ids = []
|
||||
|
|
@ -106,6 +106,8 @@ class MusicBrainzCollectionPlugin(BeetsPlugin):
|
|||
self._log.info(u'skipping invalid MBID: {0}', aid)
|
||||
|
||||
# Submit to MusicBrainz.
|
||||
self._log.info('Updating MusicBrainz collection {0}...', collection_id)
|
||||
self._log.info(
|
||||
u'Updating MusicBrainz collection {0}...', collection_id
|
||||
)
|
||||
submit_albums(collection_id, album_ids)
|
||||
self._log.info('...MusicBrainz collection updated.')
|
||||
self._log.info(u'...MusicBrainz collection updated.')
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ implemented by MusicBrainz yet.
|
|||
[1] http://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
|
||||
from beets.autotag import Recommendation
|
||||
|
|
@ -54,7 +53,7 @@ class MBSubmitPlugin(BeetsPlugin):
|
|||
|
||||
def before_choose_candidate_event(self, session, task):
|
||||
if task.rec <= self.threshold:
|
||||
return [PromptChoice('p', 'Print tracks', self.print_tracks)]
|
||||
return [PromptChoice(u'p', u'Print tracks', self.print_tracks)]
|
||||
|
||||
def print_tracks(self, session, task):
|
||||
for i in task.items:
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Update library's tags using MusicBrainz.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import autotag, library, ui, util
|
||||
|
|
@ -43,18 +42,20 @@ class MBSyncPlugin(BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('mbsync',
|
||||
help='update metadata from musicbrainz')
|
||||
cmd.parser.add_option('-p', '--pretend', action='store_true',
|
||||
help='show all changes but do nothing')
|
||||
cmd.parser.add_option('-m', '--move', action='store_true',
|
||||
dest='move',
|
||||
help="move files in the library directory")
|
||||
cmd.parser.add_option('-M', '--nomove', action='store_false',
|
||||
dest='move',
|
||||
help="don't move files in library")
|
||||
cmd.parser.add_option('-W', '--nowrite', action='store_false',
|
||||
default=None, dest='write',
|
||||
help="don't write updated metadata to files")
|
||||
help=u'update metadata from musicbrainz')
|
||||
cmd.parser.add_option(
|
||||
u'-p', u'--pretend', action='store_true',
|
||||
help=u'show all changes but do nothing')
|
||||
cmd.parser.add_option(
|
||||
u'-m', u'--move', action='store_true', dest='move',
|
||||
help=u"move files in the library directory")
|
||||
cmd.parser.add_option(
|
||||
u'-M', u'--nomove', action='store_false', dest='move',
|
||||
help=u"don't move files in library")
|
||||
cmd.parser.add_option(
|
||||
u'-W', u'--nowrite', action='store_false',
|
||||
default=None, dest='write',
|
||||
help=u"don't write updated metadata to files")
|
||||
cmd.parser.add_format_option()
|
||||
cmd.func = self.func
|
||||
return [cmd]
|
||||
|
|
@ -140,7 +141,7 @@ class MBSyncPlugin(BeetsPlugin):
|
|||
break
|
||||
|
||||
# Apply.
|
||||
self._log.debug('applying changes to {}', album_formatted)
|
||||
self._log.debug(u'applying changes to {}', album_formatted)
|
||||
with lib.transaction():
|
||||
autotag.apply_metadata(album_info, mapping)
|
||||
changed = False
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""List missing tracks.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.autotag import hooks
|
||||
from beets.library import Item
|
||||
|
|
@ -95,12 +94,12 @@ class MissingPlugin(BeetsPlugin):
|
|||
self._command = Subcommand('missing',
|
||||
help=__doc__,
|
||||
aliases=['miss'])
|
||||
self._command.parser.add_option('-c', '--count', dest='count',
|
||||
action='store_true',
|
||||
help='count missing tracks per album')
|
||||
self._command.parser.add_option('-t', '--total', dest='total',
|
||||
action='store_true',
|
||||
help='count total of missing tracks')
|
||||
self._command.parser.add_option(
|
||||
u'-c', u'--count', dest='count', action='store_true',
|
||||
help=u'count missing tracks per album')
|
||||
self._command.parser.add_option(
|
||||
u'-t', u'--total', dest='total', action='store_true',
|
||||
help=u'count total of missing tracks')
|
||||
self._command.parser.add_format_option()
|
||||
|
||||
def commands(self):
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import mpd
|
||||
import socket
|
||||
|
|
@ -80,7 +79,7 @@ class MPDClientWrapper(object):
|
|||
try:
|
||||
self.client.connect(host, port)
|
||||
except socket.error as e:
|
||||
raise ui.UserError('could not connect to MPD: {0}'.format(e))
|
||||
raise ui.UserError(u'could not connect to MPD: {0}'.format(e))
|
||||
|
||||
password = mpd_config['password'].get(unicode)
|
||||
if password:
|
||||
|
|
@ -88,7 +87,7 @@ class MPDClientWrapper(object):
|
|||
self.client.password(password)
|
||||
except mpd.CommandError as e:
|
||||
raise ui.UserError(
|
||||
'could not authenticate to MPD: {0}'.format(e)
|
||||
u'could not authenticate to MPD: {0}'.format(e)
|
||||
)
|
||||
|
||||
def disconnect(self):
|
||||
|
|
@ -338,16 +337,16 @@ class MPDStatsPlugin(plugins.BeetsPlugin):
|
|||
def commands(self):
|
||||
cmd = ui.Subcommand(
|
||||
'mpdstats',
|
||||
help='run a MPD client to gather play statistics')
|
||||
help=u'run a MPD client to gather play statistics')
|
||||
cmd.parser.add_option(
|
||||
'--host', dest='host', type='string',
|
||||
help='set the hostname of the server to connect to')
|
||||
u'--host', dest='host', type='string',
|
||||
help=u'set the hostname of the server to connect to')
|
||||
cmd.parser.add_option(
|
||||
'--port', dest='port', type='int',
|
||||
help='set the port of the MPD server to connect to')
|
||||
u'--port', dest='port', type='int',
|
||||
help=u'set the port of the MPD server to connect to')
|
||||
cmd.parser.add_option(
|
||||
'--password', dest='password', type='string',
|
||||
help='set the password of the MPD server to connect to')
|
||||
u'--password', dest='password', type='string',
|
||||
help=u'set the password of the MPD server to connect to')
|
||||
|
||||
def func(lib, opts, args):
|
||||
mpd_config.set_args(opts)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ Put something like the following in your config.yaml to configure:
|
|||
port: 6600
|
||||
password: seekrit
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
import os
|
||||
|
|
@ -126,4 +125,4 @@ class MPDUpdatePlugin(BeetsPlugin):
|
|||
|
||||
s.send('close\n')
|
||||
s.close()
|
||||
self._log.info('Database updated.')
|
||||
self._log.info(u'Database updated.')
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
"""Fixes file permissions after the file gets written on import. Put something
|
||||
like the following in your config.yaml to configure:
|
||||
|
|
@ -82,7 +81,7 @@ def permissions(lib, item=None, album=None):
|
|||
|
||||
# Checks if the destination path has the permissions configured.
|
||||
if not check_permissions(util.bytestring_path(path), file_perm):
|
||||
message = 'There was a problem setting permission on {}'.format(
|
||||
message = u'There was a problem setting permission on {}'.format(
|
||||
path)
|
||||
print(message)
|
||||
|
||||
|
|
@ -98,6 +97,6 @@ def permissions(lib, item=None, album=None):
|
|||
|
||||
# Checks if the destination path has the permissions configured.
|
||||
if not check_permissions(util.bytestring_path(path), dir_perm):
|
||||
message = 'There was a problem setting permission on {}'.format(
|
||||
message = u'There was a problem setting permission on {}'.format(
|
||||
path)
|
||||
print(message)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Send the results of a query to the configured music player as a playlist.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand
|
||||
|
|
@ -49,13 +48,13 @@ class PlayPlugin(BeetsPlugin):
|
|||
def commands(self):
|
||||
play_command = Subcommand(
|
||||
'play',
|
||||
help='send music to a player as a playlist'
|
||||
help=u'send music to a player as a playlist'
|
||||
)
|
||||
play_command.parser.add_album_option()
|
||||
play_command.parser.add_option(
|
||||
'-A', '--args',
|
||||
u'-A', u'--args',
|
||||
action='store',
|
||||
help='add additional arguments to the command',
|
||||
help=u'add additional arguments to the command',
|
||||
)
|
||||
play_command.func = self.play_music
|
||||
return [play_command]
|
||||
|
|
@ -90,7 +89,7 @@ class PlayPlugin(BeetsPlugin):
|
|||
if ARGS_MARKER in command_str:
|
||||
command_str = command_str.replace(ARGS_MARKER, opts.args)
|
||||
else:
|
||||
command_str = "{} {}".format(command_str, opts.args)
|
||||
command_str = u"{} {}".format(command_str, opts.args)
|
||||
|
||||
# Perform search by album and add folders rather than tracks to
|
||||
# playlist.
|
||||
|
|
@ -119,16 +118,15 @@ class PlayPlugin(BeetsPlugin):
|
|||
|
||||
if not selection:
|
||||
ui.print_(ui.colorize('text_warning',
|
||||
'No {0} to play.'.format(item_type)))
|
||||
u'No {0} to play.'.format(item_type)))
|
||||
return
|
||||
|
||||
# Warn user before playing any huge playlists.
|
||||
if warning_threshold and len(selection) > warning_threshold:
|
||||
ui.print_(ui.colorize(
|
||||
'text_warning',
|
||||
'You are about to queue {0} {1}.'.format(len(selection),
|
||||
item_type)
|
||||
))
|
||||
u'You are about to queue {0} {1}.'.format(
|
||||
len(selection), item_type)))
|
||||
|
||||
if ui.input_options(('Continue', 'Abort')) == 'a':
|
||||
return
|
||||
|
|
@ -139,13 +137,13 @@ class PlayPlugin(BeetsPlugin):
|
|||
else:
|
||||
open_args = [self._create_tmp_playlist(paths)]
|
||||
|
||||
self._log.debug('executing command: {} {}', command_str,
|
||||
self._log.debug(u'executing command: {} {}', command_str,
|
||||
b' '.join(open_args))
|
||||
try:
|
||||
util.interactive_open(open_args, command_str)
|
||||
except OSError as exc:
|
||||
raise ui.UserError("Could not play the query: "
|
||||
"{0}".format(exc))
|
||||
raise ui.UserError(
|
||||
"Could not play the query: {0}".format(exc))
|
||||
|
||||
def _create_tmp_playlist(self, paths_list):
|
||||
"""Create a temporary .m3u file. Return the filename.
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ Put something like the following in your config.yaml to configure:
|
|||
port: 32400
|
||||
token: token
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import requests
|
||||
from urlparse import urljoin
|
||||
|
|
@ -78,7 +77,7 @@ class PlexUpdate(BeetsPlugin):
|
|||
def update(self, lib):
|
||||
"""When the client exists try to send refresh request to Plex server.
|
||||
"""
|
||||
self._log.info('Updating Plex library...')
|
||||
self._log.info(u'Updating Plex library...')
|
||||
|
||||
# Try to send update request.
|
||||
try:
|
||||
|
|
@ -87,7 +86,7 @@ class PlexUpdate(BeetsPlugin):
|
|||
config['plex']['port'].get(),
|
||||
config['plex']['token'].get(),
|
||||
config['plex']['library_name'].get())
|
||||
self._log.info('... started.')
|
||||
self._log.info(u'... started.')
|
||||
|
||||
except requests.exceptions.RequestException:
|
||||
self._log.warning('Update failed.')
|
||||
self._log.warning(u'Update failed.')
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Get a random song or album from the library.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand, decargs, print_
|
||||
|
|
@ -66,11 +65,13 @@ def random_item(lib, opts, args):
|
|||
print_(format(item))
|
||||
|
||||
random_cmd = Subcommand('random',
|
||||
help='chose a random track or album')
|
||||
random_cmd.parser.add_option('-n', '--number', action='store', type="int",
|
||||
help='number of objects to choose', default=1)
|
||||
random_cmd.parser.add_option('-e', '--equal-chance', action='store_true',
|
||||
help='each artist has the same chance')
|
||||
help=u'chose a random track or album')
|
||||
random_cmd.parser.add_option(
|
||||
u'-n', u'--number', action='store', type="int",
|
||||
help=u'number of objects to choose', default=1)
|
||||
random_cmd.parser.add_option(
|
||||
u'-e', u'--equal-chance', action='store_true',
|
||||
help=u'each artist has the same chance')
|
||||
random_cmd.parser.add_all_common_options()
|
||||
random_cmd.func = random_item
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
|
|
@ -56,13 +55,13 @@ def call(args):
|
|||
return command_output(args)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise ReplayGainError(
|
||||
"{0} exited with status {1}".format(args[0], e.returncode)
|
||||
u"{0} exited with status {1}".format(args[0], e.returncode)
|
||||
)
|
||||
except UnicodeEncodeError:
|
||||
# Due to a bug in Python 2's subprocess on Windows, Unicode
|
||||
# filenames can fail to encode on that platform. See:
|
||||
# http://code.google.com/p/beets/issues/detail?id=499
|
||||
raise ReplayGainError("argument encoding failed")
|
||||
raise ReplayGainError(u"argument encoding failed")
|
||||
|
||||
|
||||
# Backend base and plumbing classes.
|
||||
|
|
@ -111,11 +110,11 @@ class Bs1770gainBackend(Backend):
|
|||
self.command = cmd
|
||||
except OSError:
|
||||
raise FatalReplayGainError(
|
||||
'Is bs1770gain installed? Is your method in config correct?'
|
||||
u'Is bs1770gain installed? Is your method in config correct?'
|
||||
)
|
||||
if not self.command:
|
||||
raise FatalReplayGainError(
|
||||
'no replaygain command found: install bs1770gain'
|
||||
u'no replaygain command found: install bs1770gain'
|
||||
)
|
||||
|
||||
def compute_track_gain(self, items):
|
||||
|
|
@ -137,7 +136,7 @@ class Bs1770gainBackend(Backend):
|
|||
output = self.compute_gain(supported_items, True)
|
||||
|
||||
if not output:
|
||||
raise ReplayGainError('no output from bs1770gain')
|
||||
raise ReplayGainError(u'no output from bs1770gain')
|
||||
return AlbumGain(output[-1], output[:-1])
|
||||
|
||||
def isplitter(self, items, chunk_at):
|
||||
|
|
@ -204,7 +203,9 @@ class Bs1770gainBackend(Backend):
|
|||
args = cmd + [syspath(i.path, prefix=False) for i in items]
|
||||
|
||||
# Invoke the command.
|
||||
self._log.debug("executing {0}", " ".join(map(displayable_path, args)))
|
||||
self._log.debug(
|
||||
u'executing {0}', u' '.join(map(displayable_path, args))
|
||||
)
|
||||
output = call(args)
|
||||
|
||||
self._log.debug(u'analysis finished: {0}', output)
|
||||
|
|
@ -228,8 +229,8 @@ class Bs1770gainBackend(Backend):
|
|||
for parts in results[0:num_lines]:
|
||||
part = parts.split(b'\n')
|
||||
if len(part) == 0:
|
||||
self._log.debug('bad tool output: {0!r}', text)
|
||||
raise ReplayGainError('bs1770gain failed')
|
||||
self._log.debug(u'bad tool output: {0!r}', text)
|
||||
raise ReplayGainError(u'bs1770gain failed')
|
||||
|
||||
try:
|
||||
song = {
|
||||
|
|
@ -238,7 +239,7 @@ class Bs1770gainBackend(Backend):
|
|||
'peak': float(part[2].split('/')[1]),
|
||||
}
|
||||
except IndexError:
|
||||
self._log.info('bs1770gain reports (faulty file?): {}', parts)
|
||||
self._log.info(u'bs1770gain reports (faulty file?): {}', parts)
|
||||
continue
|
||||
|
||||
out.append(Gain(song['gain'], song['peak']))
|
||||
|
|
@ -261,9 +262,8 @@ class CommandBackend(Backend):
|
|||
# Explicit executable path.
|
||||
if not os.path.isfile(self.command):
|
||||
raise FatalReplayGainError(
|
||||
'replaygain command does not exist: {0}'.format(
|
||||
self.command
|
||||
)
|
||||
u'replaygain command does not exist: {0}'.format(
|
||||
self.command)
|
||||
)
|
||||
else:
|
||||
# Check whether the program is in $PATH.
|
||||
|
|
@ -275,7 +275,7 @@ class CommandBackend(Backend):
|
|||
pass
|
||||
if not self.command:
|
||||
raise FatalReplayGainError(
|
||||
'no replaygain command found: install mp3gain or aacgain'
|
||||
u'no replaygain command found: install mp3gain or aacgain'
|
||||
)
|
||||
|
||||
self.noclip = config['noclip'].get(bool)
|
||||
|
|
@ -322,7 +322,7 @@ class CommandBackend(Backend):
|
|||
the album gain
|
||||
"""
|
||||
if len(items) == 0:
|
||||
self._log.debug('no supported tracks to analyze')
|
||||
self._log.debug(u'no supported tracks to analyze')
|
||||
return []
|
||||
|
||||
"""Compute ReplayGain values and return a list of results
|
||||
|
|
@ -361,7 +361,7 @@ class CommandBackend(Backend):
|
|||
parts = line.split(b'\t')
|
||||
if len(parts) != 6 or parts[0] == b'File':
|
||||
self._log.debug(u'bad tool output: {0}', text)
|
||||
raise ReplayGainError('mp3gain failed')
|
||||
raise ReplayGainError(u'mp3gain failed')
|
||||
d = {
|
||||
'file': parts[0],
|
||||
'mp3gain': int(parts[1]),
|
||||
|
|
@ -397,7 +397,7 @@ class GStreamerBackend(Backend):
|
|||
if self._src is None or self._decbin is None or self._conv is None \
|
||||
or self._res is None or self._rg is None:
|
||||
raise FatalGstreamerPluginReplayGainError(
|
||||
"Failed to load required GStreamer plugins"
|
||||
u"Failed to load required GStreamer plugins"
|
||||
)
|
||||
|
||||
# We check which files need gain ourselves, so all files given
|
||||
|
|
@ -444,14 +444,14 @@ class GStreamerBackend(Backend):
|
|||
import gi
|
||||
except ImportError:
|
||||
raise FatalReplayGainError(
|
||||
"Failed to load GStreamer: python-gi not found"
|
||||
u"Failed to load GStreamer: python-gi not found"
|
||||
)
|
||||
|
||||
try:
|
||||
gi.require_version('Gst', '1.0')
|
||||
except ValueError as e:
|
||||
raise FatalReplayGainError(
|
||||
"Failed to load GStreamer 1.0: {0}".format(e)
|
||||
u"Failed to load GStreamer 1.0: {0}".format(e)
|
||||
)
|
||||
|
||||
from gi.repository import GObject, Gst, GLib
|
||||
|
|
@ -486,7 +486,7 @@ class GStreamerBackend(Backend):
|
|||
def compute_track_gain(self, items):
|
||||
self.compute(items, False)
|
||||
if len(self._file_tags) != len(items):
|
||||
raise ReplayGainError("Some tracks did not receive tags")
|
||||
raise ReplayGainError(u"Some tracks did not receive tags")
|
||||
|
||||
ret = []
|
||||
for item in items:
|
||||
|
|
@ -499,7 +499,7 @@ class GStreamerBackend(Backend):
|
|||
items = list(album.items())
|
||||
self.compute(items, True)
|
||||
if len(self._file_tags) != len(items):
|
||||
raise ReplayGainError("Some items in album did not receive tags")
|
||||
raise ReplayGainError(u"Some items in album did not receive tags")
|
||||
|
||||
# Collect track gains.
|
||||
track_gains = []
|
||||
|
|
@ -508,7 +508,7 @@ class GStreamerBackend(Backend):
|
|||
gain = self._file_tags[item]["TRACK_GAIN"]
|
||||
peak = self._file_tags[item]["TRACK_PEAK"]
|
||||
except KeyError:
|
||||
raise ReplayGainError("results missing for track")
|
||||
raise ReplayGainError(u"results missing for track")
|
||||
track_gains.append(Gain(gain, peak))
|
||||
|
||||
# Get album gain information from the last track.
|
||||
|
|
@ -517,7 +517,7 @@ class GStreamerBackend(Backend):
|
|||
gain = last_tags["ALBUM_GAIN"]
|
||||
peak = last_tags["ALBUM_PEAK"]
|
||||
except KeyError:
|
||||
raise ReplayGainError("results missing for album")
|
||||
raise ReplayGainError(u"results missing for album")
|
||||
|
||||
return AlbumGain(Gain(gain, peak), track_gains)
|
||||
|
||||
|
|
@ -539,7 +539,7 @@ class GStreamerBackend(Backend):
|
|||
f = self._src.get_property("location")
|
||||
# A GStreamer error, either an unsupported format or a bug.
|
||||
self._error = ReplayGainError(
|
||||
"Error {0!r} - {1!r} on file {2!r}".format(err, debug, f)
|
||||
u"Error {0!r} - {1!r} on file {2!r}".format(err, debug, f)
|
||||
)
|
||||
|
||||
def _on_tag(self, bus, message):
|
||||
|
|
@ -663,7 +663,7 @@ class AudioToolsBackend(Backend):
|
|||
import audiotools.replaygain
|
||||
except ImportError:
|
||||
raise FatalReplayGainError(
|
||||
"Failed to load audiotools: audiotools not found"
|
||||
u"Failed to load audiotools: audiotools not found"
|
||||
)
|
||||
self._mod_audiotools = audiotools
|
||||
self._mod_replaygain = audiotools.replaygain
|
||||
|
|
@ -681,11 +681,11 @@ class AudioToolsBackend(Backend):
|
|||
audiofile = self._mod_audiotools.open(item.path)
|
||||
except IOError:
|
||||
raise ReplayGainError(
|
||||
"File {} was not found".format(item.path)
|
||||
u"File {} was not found".format(item.path)
|
||||
)
|
||||
except self._mod_audiotools.UnsupportedFile:
|
||||
raise ReplayGainError(
|
||||
"Unsupported file type {}".format(item.format)
|
||||
u"Unsupported file type {}".format(item.format)
|
||||
)
|
||||
|
||||
return audiofile
|
||||
|
|
@ -704,8 +704,7 @@ class AudioToolsBackend(Backend):
|
|||
rg = self._mod_replaygain.ReplayGain(audiofile.sample_rate())
|
||||
except ValueError:
|
||||
raise ReplayGainError(
|
||||
"Unsupported sample rate {}".format(item.samplerate)
|
||||
)
|
||||
u"Unsupported sample rate {}".format(item.samplerate))
|
||||
return
|
||||
return rg
|
||||
|
||||
|
|
@ -730,8 +729,8 @@ class AudioToolsBackend(Backend):
|
|||
except ValueError as exc:
|
||||
# `audiotools.replaygain` can raise a `ValueError` if the sample
|
||||
# rate is incorrect.
|
||||
self._log.debug('error in rg.title_gain() call: {}', exc)
|
||||
raise ReplayGainError('audiotools audio data error')
|
||||
self._log.debug(u'error in rg.title_gain() call: {}', exc)
|
||||
raise ReplayGainError(u'audiotools audio data error')
|
||||
|
||||
def _compute_track_gain(self, item):
|
||||
"""Compute ReplayGain value for the requested item.
|
||||
|
|
@ -830,8 +829,7 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
)
|
||||
except (ReplayGainError, FatalReplayGainError) as e:
|
||||
raise ui.UserError(
|
||||
'replaygain initialization failed: {0}'.format(e)
|
||||
)
|
||||
u'replaygain initialization failed: {0}'.format(e))
|
||||
|
||||
def track_requires_gain(self, item):
|
||||
return self.overwrite or \
|
||||
|
|
@ -894,8 +892,7 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
self._log.info(u"ReplayGain error: {0}", e)
|
||||
except FatalReplayGainError as e:
|
||||
raise ui.UserError(
|
||||
u"Fatal replay gain error: {0}".format(e)
|
||||
)
|
||||
u"Fatal replay gain error: {0}".format(e))
|
||||
|
||||
def handle_track(self, item, write):
|
||||
"""Compute track replay gain and store it in the item.
|
||||
|
|
@ -924,8 +921,7 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
self._log.info(u"ReplayGain error: {0}", e)
|
||||
except FatalReplayGainError as e:
|
||||
raise ui.UserError(
|
||||
u"Fatal replay gain error: {0}".format(e)
|
||||
)
|
||||
u"Fatal replay gain error: {0}".format(e))
|
||||
|
||||
def imported(self, session, task):
|
||||
"""Add replay gain info to items or albums of ``task``.
|
||||
|
|
@ -951,7 +947,7 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
for item in lib.items(ui.decargs(args)):
|
||||
self.handle_track(item, write)
|
||||
|
||||
cmd = ui.Subcommand('replaygain', help='analyze for ReplayGain')
|
||||
cmd = ui.Subcommand('replaygain', help=u'analyze for ReplayGain')
|
||||
cmd.parser.add_album_option()
|
||||
cmd.func = func
|
||||
return [cmd]
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Uses user-specified rewriting rules to canonicalize names for path
|
||||
formats.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
|
@ -56,9 +55,9 @@ class RewritePlugin(BeetsPlugin):
|
|||
try:
|
||||
fieldname, pattern = key.split(None, 1)
|
||||
except ValueError:
|
||||
raise ui.UserError("invalid rewrite specification")
|
||||
raise ui.UserError(u"invalid rewrite specification")
|
||||
if fieldname not in library.Item._fields:
|
||||
raise ui.UserError("invalid field name (%s) in rewriter" %
|
||||
raise ui.UserError(u"invalid field name (%s) in rewriter" %
|
||||
fieldname)
|
||||
self._log.debug(u'adding template field {0}', key)
|
||||
pattern = re.compile(pattern.lower())
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
automatically whenever tags are written.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
|
|
@ -64,10 +63,11 @@ class ScrubPlugin(BeetsPlugin):
|
|||
util.displayable_path(item.path))
|
||||
self._scrub_item(item, opts.write)
|
||||
|
||||
scrub_cmd = ui.Subcommand('scrub', help='clean audio tags')
|
||||
scrub_cmd.parser.add_option('-W', '--nowrite', dest='write',
|
||||
action='store_false', default=True,
|
||||
help='leave tags empty')
|
||||
scrub_cmd = ui.Subcommand('scrub', help=u'clean audio tags')
|
||||
scrub_cmd.parser.add_option(
|
||||
u'-W', u'--nowrite', dest='write',
|
||||
action='store_false', default=True,
|
||||
help=u'leave tags empty')
|
||||
scrub_cmd.func = scrub_func
|
||||
|
||||
return [scrub_cmd]
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
"""Generates smart playlists based on beets queries.
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
|
|
@ -46,9 +45,11 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
self.register_listener('database_change', self.db_change)
|
||||
|
||||
def commands(self):
|
||||
spl_update = ui.Subcommand('splupdate',
|
||||
help='update the smart playlists. Playlist '
|
||||
'names may be passed as arguments.')
|
||||
spl_update = ui.Subcommand(
|
||||
'splupdate',
|
||||
help=u'update the smart playlists. Playlist names may be '
|
||||
u'passed as arguments.'
|
||||
)
|
||||
spl_update.func = self.update_cmd
|
||||
return [spl_update]
|
||||
|
||||
|
|
@ -64,9 +65,10 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
for name, q, a_q in self._unmatched_playlists
|
||||
if name in args)
|
||||
if not playlists:
|
||||
raise ui.UserError('No playlist matching any of {0} '
|
||||
'found'.format([name for name, _, _ in
|
||||
self._unmatched_playlists]))
|
||||
raise ui.UserError(
|
||||
u'No playlist matching any of {0} found'.format(
|
||||
[name for name, _, _ in self._unmatched_playlists])
|
||||
)
|
||||
|
||||
self._matched_playlists = playlists
|
||||
self._unmatched_playlists -= playlists
|
||||
|
|
@ -95,7 +97,7 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
|
||||
for playlist in self.config['playlists'].get(list):
|
||||
if 'name' not in playlist:
|
||||
self._log.warn("playlist configuration is missing name")
|
||||
self._log.warn(u"playlist configuration is missing name")
|
||||
continue
|
||||
|
||||
playlist_data = (playlist['name'],)
|
||||
|
|
@ -131,7 +133,7 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
playlist_data += (query_and_sort,)
|
||||
|
||||
except ParsingError as exc:
|
||||
self._log.warn("invalid query in playlist {}: {}",
|
||||
self._log.warn(u"invalid query in playlist {}: {}",
|
||||
playlist['name'], exc)
|
||||
continue
|
||||
|
||||
|
|
@ -151,14 +153,15 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
for playlist in self._unmatched_playlists:
|
||||
n, (q, _), (a_q, _) = playlist
|
||||
if self.matches(model, q, a_q):
|
||||
self._log.debug("{0} will be updated because of {1}", n, model)
|
||||
self._log.debug(
|
||||
u"{0} will be updated because of {1}", n, model)
|
||||
self._matched_playlists.add(playlist)
|
||||
self.register_listener('cli_exit', self.update_playlists)
|
||||
|
||||
self._unmatched_playlists -= self._matched_playlists
|
||||
|
||||
def update_playlists(self, lib):
|
||||
self._log.info("Updating {0} smart playlists...",
|
||||
self._log.info(u"Updating {0} smart playlists...",
|
||||
len(self._matched_playlists))
|
||||
|
||||
playlist_dir = self.config['playlist_dir'].as_filename()
|
||||
|
|
@ -196,4 +199,4 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
with open(syspath(m3u_path), 'w') as f:
|
||||
for path in m3us[m3u]:
|
||||
f.write(path + b'\n')
|
||||
self._log.info("{0} playlists updated", len(self._matched_playlists))
|
||||
self._log.info(u"{0} playlists updated", len(self._matched_playlists))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
import webbrowser
|
||||
|
|
@ -41,17 +40,17 @@ class SpotifyPlugin(BeetsPlugin):
|
|||
self.output_results(results)
|
||||
spotify_cmd = ui.Subcommand(
|
||||
'spotify',
|
||||
help='build a Spotify playlist'
|
||||
help=u'build a Spotify playlist'
|
||||
)
|
||||
spotify_cmd.parser.add_option(
|
||||
'-m', '--mode', action='store',
|
||||
help='"open" to open Spotify with playlist, '
|
||||
'"list" to print (default)'
|
||||
u'-m', u'--mode', action='store',
|
||||
help=u'"open" to open Spotify with playlist, '
|
||||
u'"list" to print (default)'
|
||||
)
|
||||
spotify_cmd.parser.add_option(
|
||||
'-f', '--show-failures', action='store_true',
|
||||
help='list tracks that did not match a Spotify ID',
|
||||
dest='show_failures',
|
||||
u'-f', u'--show-failures',
|
||||
action='store_true', dest='show_failures',
|
||||
help=u'list tracks that did not match a Spotify ID'
|
||||
)
|
||||
spotify_cmd.func = queries
|
||||
return [spotify_cmd]
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Moves patterns in path formats (suitable for moving articles)."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
from beets.plugins import BeetsPlugin
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ This plugin is POSIX-only.
|
|||
Spec: standards.freedesktop.org/thumbnail-spec/latest/index.html
|
||||
"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from hashlib import md5
|
||||
import os
|
||||
|
|
@ -58,14 +57,15 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
thumbnails_command = Subcommand("thumbnails",
|
||||
help="Create album thumbnails")
|
||||
help=u"Create album thumbnails")
|
||||
thumbnails_command.parser.add_option(
|
||||
'-f', '--force', dest='force', action='store_true', default=False,
|
||||
help='force regeneration of thumbnails deemed fine (existing & '
|
||||
'recent enough)')
|
||||
u'-f', u'--force',
|
||||
dest='force', action='store_true', default=False,
|
||||
help=u'force regeneration of thumbnails deemed fine (existing & '
|
||||
u'recent enough)')
|
||||
thumbnails_command.parser.add_option(
|
||||
'--dolphin', dest='dolphin', action='store_true', default=False,
|
||||
help="create Dolphin-compatible thumbnail information (for KDE)")
|
||||
u'--dolphin', dest='dolphin', action='store_true', default=False,
|
||||
help=u"create Dolphin-compatible thumbnail information (for KDE)")
|
||||
thumbnails_command.func = self.process_query
|
||||
|
||||
return [thumbnails_command]
|
||||
|
|
@ -84,8 +84,8 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
- detect whether we'll use GIO or Python to get URIs
|
||||
"""
|
||||
if not ArtResizer.shared.local:
|
||||
self._log.warning("No local image resizing capabilities, "
|
||||
"cannot generate thumbnails")
|
||||
self._log.warning(u"No local image resizing capabilities, "
|
||||
u"cannot generate thumbnails")
|
||||
return False
|
||||
|
||||
for dir in (NORMAL_DIR, LARGE_DIR):
|
||||
|
|
@ -99,12 +99,12 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
assert has_PIL() # since we're local
|
||||
self.write_metadata = write_metadata_pil
|
||||
tool = "PIL"
|
||||
self._log.debug("using {0} to write metadata", tool)
|
||||
self._log.debug(u"using {0} to write metadata", tool)
|
||||
|
||||
uri_getter = GioURI()
|
||||
if not uri_getter.available:
|
||||
uri_getter = PathlibURI()
|
||||
self._log.debug("using {0.name} to compute URIs", uri_getter)
|
||||
self._log.debug(u"using {0.name} to compute URIs", uri_getter)
|
||||
self.get_uri = uri_getter.uri
|
||||
|
||||
return True
|
||||
|
|
@ -122,7 +122,7 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
|
||||
size = ArtResizer.shared.get_size(album.artpath)
|
||||
if not size:
|
||||
self._log.warning('problem getting the picture size for {0}',
|
||||
self._log.warning(u'problem getting the picture size for {0}',
|
||||
album.artpath)
|
||||
return
|
||||
|
||||
|
|
@ -132,9 +132,9 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
wrote &= self.make_cover_thumbnail(album, 128, NORMAL_DIR)
|
||||
|
||||
if wrote:
|
||||
self._log.info('wrote thumbnail for {0}', album)
|
||||
self._log.info(u'wrote thumbnail for {0}', album)
|
||||
else:
|
||||
self._log.info('nothing to do for {0}', album)
|
||||
self._log.info(u'nothing to do for {0}', album)
|
||||
|
||||
def make_cover_thumbnail(self, album, size, target_dir):
|
||||
"""Make a thumbnail of given size for `album` and put it in
|
||||
|
|
@ -145,11 +145,11 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
if os.path.exists(target) and \
|
||||
os.stat(target).st_mtime > os.stat(album.artpath).st_mtime:
|
||||
if self.config['force']:
|
||||
self._log.debug("found a suitable {1}x{1} thumbnail for {0}, "
|
||||
"forcing regeneration", album, size)
|
||||
self._log.debug(u"found a suitable {1}x{1} thumbnail for {0}, "
|
||||
u"forcing regeneration", album, size)
|
||||
else:
|
||||
self._log.debug("{1}x{1} thumbnail for {0} exists and is "
|
||||
"recent enough", album, size)
|
||||
self._log.debug(u"{1}x{1} thumbnail for {0} exists and is "
|
||||
u"recent enough", album, size)
|
||||
return False
|
||||
resized = ArtResizer.shared.resize(size, album.artpath,
|
||||
util.syspath(target))
|
||||
|
|
@ -174,7 +174,7 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
try:
|
||||
self.write_metadata(image_path, metadata)
|
||||
except Exception:
|
||||
self._log.exception("could not write metadata to {0}",
|
||||
self._log.exception(u"could not write metadata to {0}",
|
||||
util.displayable_path(image_path))
|
||||
|
||||
def make_dolphin_cover_thumbnail(self, album):
|
||||
|
|
@ -185,7 +185,7 @@ class ThumbnailsPlugin(BeetsPlugin):
|
|||
with open(outfilename, 'w') as f:
|
||||
f.write(b"[Desktop Entry]\nIcon=./{0}".format(artfile))
|
||||
f.close()
|
||||
self._log.debug("Wrote file {0}", util.displayable_path(outfilename))
|
||||
self._log.debug(u"Wrote file {0}", util.displayable_path(outfilename))
|
||||
|
||||
|
||||
def write_metadata_im(file, metadata):
|
||||
|
|
@ -266,7 +266,7 @@ class GioURI(URIGetter):
|
|||
def uri(self, path):
|
||||
g_file_ptr = self.libgio.g_file_new_for_path(path)
|
||||
if not g_file_ptr:
|
||||
raise RuntimeError("No gfile pointer received for {0}".format(
|
||||
raise RuntimeError(u"No gfile pointer received for {0}".format(
|
||||
util.displayable_path(path)))
|
||||
|
||||
try:
|
||||
|
|
@ -277,8 +277,8 @@ class GioURI(URIGetter):
|
|||
self.libgio.g_object_unref(g_file_ptr)
|
||||
if not uri_ptr:
|
||||
self.libgio.g_free(uri_ptr)
|
||||
raise RuntimeError("No URI received from the gfile pointer for "
|
||||
"{0}".format(util.displayable_path(path)))
|
||||
raise RuntimeError(u"No URI received from the gfile pointer for "
|
||||
u"{0}".format(util.displayable_path(path)))
|
||||
|
||||
try:
|
||||
uri = copy_c_string(uri_ptr)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.dbcore import types
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""A Web interface to beets."""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
|
|
@ -264,9 +263,9 @@ class WebPlugin(BeetsPlugin):
|
|||
})
|
||||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('web', help='start a Web interface')
|
||||
cmd.parser.add_option('-d', '--debug', action='store_true',
|
||||
default=False, help='debug mode')
|
||||
cmd = ui.Subcommand('web', help=u'start a Web interface')
|
||||
cmd.parser.add_option(u'-d', u'--debug', action='store_true',
|
||||
default=False, help=u'debug mode')
|
||||
|
||||
def func(lib, opts, args):
|
||||
args = ui.decargs(args)
|
||||
|
|
@ -278,7 +277,7 @@ class WebPlugin(BeetsPlugin):
|
|||
app.config['lib'] = lib
|
||||
# Enable CORS if required.
|
||||
if self.config['cors']:
|
||||
self._log.info('Enabling CORS with origin: {0}',
|
||||
self._log.info(u'Enabling CORS with origin: {0}',
|
||||
self.config['cors'])
|
||||
from flask.ext.cors import CORS
|
||||
app.config['CORS_ALLOW_HEADERS'] = "Content-Type"
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
""" Clears tag fields in media files."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
from beets.plugins import BeetsPlugin
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""Some common functionality for beets' test cases."""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
|
@ -102,11 +101,11 @@ def album(lib=None):
|
|||
_item_ident += 1
|
||||
i = beets.library.Album(
|
||||
artpath=None,
|
||||
albumartist='some album artist',
|
||||
albumartist_sort='some sort album artist',
|
||||
albumartist_credit='some album artist credit',
|
||||
album='the album',
|
||||
genre='the genre',
|
||||
albumartist=u'some album artist',
|
||||
albumartist_sort=u'some sort album artist',
|
||||
albumartist_credit=u'some album artist credit',
|
||||
album=u'the album',
|
||||
genre=u'the genre',
|
||||
year=2014,
|
||||
month=2,
|
||||
day=5,
|
||||
|
|
@ -170,11 +169,11 @@ class TestCase(unittest.TestCase):
|
|||
|
||||
def assertExists(self, path):
|
||||
self.assertTrue(os.path.exists(path),
|
||||
'file does not exist: {!r}'.format(path))
|
||||
u'file does not exist: {!r}'.format(path))
|
||||
|
||||
def assertNotExists(self, path):
|
||||
self.assertFalse(os.path.exists(path),
|
||||
'file exists: {!r}'.format((path)))
|
||||
u'file exists: {!r}'.format((path)))
|
||||
|
||||
|
||||
class LibTestCase(TestCase):
|
||||
|
|
@ -228,7 +227,7 @@ class InputException(Exception):
|
|||
def __str__(self):
|
||||
msg = "Attempt to read with no input provided."
|
||||
if self.output is not None:
|
||||
msg += " Output: %s" % self.output
|
||||
msg += " Output: {!r}".format(self.output)
|
||||
return msg
|
||||
|
||||
|
||||
|
|
@ -352,5 +351,5 @@ def slow_test(unused=None):
|
|||
def _id(obj):
|
||||
return obj
|
||||
if 'SKIP_SLOW_TESTS' in os.environ:
|
||||
return unittest.skip('test is slow')
|
||||
return unittest.skip(u'test is slow')
|
||||
return _id
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ information or mock the environment.
|
|||
"""
|
||||
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
|
@ -244,7 +243,7 @@ class TestHelper(object):
|
|||
track_no = 0
|
||||
album_item_count = item_count
|
||||
while album_item_count:
|
||||
title = 'track {0}'.format(track_no)
|
||||
title = u'track {0}'.format(track_no)
|
||||
src = os.path.join(_common.RSRC, 'full.mp3')
|
||||
dest = os.path.join(album_dir, '{0}.mp3'.format(title))
|
||||
if os.path.exists(dest):
|
||||
|
|
@ -527,14 +526,14 @@ def generate_album_info(album_id, track_ids):
|
|||
"""
|
||||
tracks = [generate_track_info(id) for id in track_ids]
|
||||
album = AlbumInfo(
|
||||
album_id='album info',
|
||||
album='album info',
|
||||
artist='album info',
|
||||
artist_id='album info',
|
||||
album_id=u'album info',
|
||||
album=u'album info',
|
||||
artist=u'album info',
|
||||
artist_id=u'album info',
|
||||
tracks=tracks,
|
||||
)
|
||||
for field in ALBUM_INFO_FIELDS:
|
||||
setattr(album, field, 'album info')
|
||||
setattr(album, field, u'album info')
|
||||
|
||||
return album
|
||||
|
||||
|
|
@ -553,11 +552,11 @@ def generate_track_info(track_id='track info', values={}):
|
|||
string fields are set to "track info".
|
||||
"""
|
||||
track = TrackInfo(
|
||||
title='track info',
|
||||
title=u'track info',
|
||||
track_id=track_id,
|
||||
)
|
||||
for field in TRACK_INFO_FIELDS:
|
||||
setattr(track, field, 'track info')
|
||||
setattr(track, field, u'track info')
|
||||
for field, value in values.items():
|
||||
setattr(track, field, value)
|
||||
return track
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
|
@ -45,7 +44,7 @@ def main(argv=None):
|
|||
"""
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
print('Fetching samples from:')
|
||||
print(u'Fetching samples from:')
|
||||
for s in test_lyrics.GOOGLE_SOURCES + test_lyrics.DEFAULT_SOURCES:
|
||||
print(s['url'])
|
||||
url = s['url'] + s['path']
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
|
|
@ -17,7 +16,7 @@ class TestPlugin(BeetsPlugin):
|
|||
test.func = lambda *args: None
|
||||
|
||||
# Used in CompletionTest
|
||||
test.parser.add_option('-o', '--option', dest='my_opt')
|
||||
test.parser.add_option(u'-o', u'--option', dest='my_opt')
|
||||
|
||||
plugin = ui.Subcommand('plugin')
|
||||
plugin.func = lambda *args: None
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Tests for the album art fetchers."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
|
@ -298,10 +297,10 @@ class ArtImporterTest(UseThePlugin):
|
|||
self.task.is_album = True
|
||||
self.task.album = self.album
|
||||
info = AlbumInfo(
|
||||
album='some album',
|
||||
album_id='albumid',
|
||||
artist='some artist',
|
||||
artist_id='artistid',
|
||||
album=u'some album',
|
||||
album_id=u'albumid',
|
||||
artist=u'some artist',
|
||||
artist_id=u'artistid',
|
||||
tracks=[],
|
||||
)
|
||||
self.task.set_choice(AlbumMatch(0, info, {}, set(), set()))
|
||||
|
|
@ -439,7 +438,7 @@ class ArtForAlbumTest(UseThePlugin):
|
|||
PIL (so comparisons and measurements are unavailable).
|
||||
"""
|
||||
if ArtResizer.shared.method[0] == WEBPROXY:
|
||||
self.skipTest("ArtResizer has no local imaging backend available")
|
||||
self.skipTest(u"ArtResizer has no local imaging backend available")
|
||||
|
||||
def test_respect_minwidth(self):
|
||||
self._require_backend()
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Tests for autotagging functionality.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
import copy
|
||||
|
|
@ -636,7 +635,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
|
|||
trackinfo = []
|
||||
trackinfo.append(TrackInfo(
|
||||
u'oneNew',
|
||||
'dfa939ec-118c-4d0f-84a0-60f3d1e6522c',
|
||||
u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c',
|
||||
medium=1,
|
||||
medium_index=1,
|
||||
medium_total=1,
|
||||
|
|
@ -646,7 +645,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
|
|||
))
|
||||
trackinfo.append(TrackInfo(
|
||||
u'twoNew',
|
||||
'40130ed1-a27c-42fd-a328-1ebefb6caef4',
|
||||
u'40130ed1-a27c-42fd-a328-1ebefb6caef4',
|
||||
medium=2,
|
||||
medium_index=1,
|
||||
index=2,
|
||||
|
|
@ -808,16 +807,16 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil):
|
|||
trackinfo = []
|
||||
trackinfo.append(TrackInfo(
|
||||
u'oneNew',
|
||||
'dfa939ec-118c-4d0f-84a0-60f3d1e6522c',
|
||||
u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c',
|
||||
u'artistOneNew',
|
||||
'a05686fc-9db2-4c23-b99e-77f5db3e5282',
|
||||
u'a05686fc-9db2-4c23-b99e-77f5db3e5282',
|
||||
index=1,
|
||||
))
|
||||
trackinfo.append(TrackInfo(
|
||||
u'twoNew',
|
||||
'40130ed1-a27c-42fd-a328-1ebefb6caef4',
|
||||
u'40130ed1-a27c-42fd-a328-1ebefb6caef4',
|
||||
u'artistTwoNew',
|
||||
'80b3cf5e-18fe-4c59-98c7-e5bb87210710',
|
||||
u'80b3cf5e-18fe-4c59-98c7-e5bb87210710',
|
||||
index=2,
|
||||
))
|
||||
self.info = AlbumInfo(
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Tests for the 'bucket' plugin."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from test._common import unittest
|
||||
from beetsplug import bucket
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import yaml
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
import os.path
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Test for dbcore's date-based queries.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from test import _common
|
||||
from test._common import unittest
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Tests for the DBCore database abstraction.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
|
@ -313,7 +312,7 @@ class ModelTest(unittest.TestCase):
|
|||
def test_computed_field(self):
|
||||
model = TestModelWithGetters()
|
||||
self.assertEqual(model.aComputedField, 'thing')
|
||||
with self.assertRaisesRegexp(KeyError, 'computed field .+ deleted'):
|
||||
with self.assertRaisesRegexp(KeyError, u'computed field .+ deleted'):
|
||||
del model.aComputedField
|
||||
|
||||
def test_items(self):
|
||||
|
|
@ -329,7 +328,7 @@ class ModelTest(unittest.TestCase):
|
|||
model._db
|
||||
|
||||
def test_parse_nonstring(self):
|
||||
with self.assertRaisesRegexp(TypeError, "must be a string"):
|
||||
with self.assertRaisesRegexp(TypeError, u"must be a string"):
|
||||
dbcore.Model._parse(None, 42)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os.path
|
||||
from mock import Mock, patch
|
||||
|
|
@ -31,7 +30,7 @@ class EchonestCliTest(unittest.TestCase, TestHelper):
|
|||
try:
|
||||
__import__('pyechonest')
|
||||
except ImportError:
|
||||
self.skipTest('pyechonest not available')
|
||||
self.skipTest(u'pyechonest not available')
|
||||
|
||||
self.setup_beets()
|
||||
self.load_plugins('echonest')
|
||||
|
|
@ -157,7 +156,7 @@ class EchonestCliTest(unittest.TestCase, TestHelper):
|
|||
def test_custom_field_range_query(self):
|
||||
item = Item(liveness=2.2)
|
||||
item.add(self.lib)
|
||||
item = self.lib.items('liveness:2.2..3').get()
|
||||
item = self.lib.items(u'liveness:2.2..3').get()
|
||||
self.assertEqual(item['liveness'], 2.2)
|
||||
|
||||
def profile(self, item, **values):
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
import codecs
|
||||
|
||||
from mock import patch
|
||||
|
|
@ -218,7 +217,7 @@ class EditCommandTest(unittest.TestCase, TestHelper, EditMixin):
|
|||
# Apply changes.
|
||||
['a'])
|
||||
|
||||
self.assertEqual(self.lib.items('id:1')[0].foo, 'bar')
|
||||
self.assertEqual(self.lib.items(u'id:1')[0].foo, 'bar')
|
||||
self.assertCounts(write_call_count=1,
|
||||
title_starts_with=u't\u00eftle')
|
||||
|
||||
|
|
@ -265,7 +264,7 @@ class EditCommandTest(unittest.TestCase, TestHelper, EditMixin):
|
|||
"""Edit the yaml file incorrectly (resulting in a well-formed but
|
||||
invalid yaml document)."""
|
||||
# Edit the yaml file to an invalid but parseable file.
|
||||
self.run_mocked_command({'contents': 'wellformed: yes, but invalid'},
|
||||
self.run_mocked_command({'contents': u'wellformed: yes, but invalid'},
|
||||
# No stdin.
|
||||
[])
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os.path
|
||||
import shutil
|
||||
|
|
@ -101,7 +100,7 @@ class EmbedartCliTest(_common.TestCase, TestHelper):
|
|||
|
||||
if os.path.isfile(tmp_path):
|
||||
os.remove(tmp_path)
|
||||
self.fail('Artwork file {0} was not deleted'.format(tmp_path))
|
||||
self.fail(u'Artwork file {0} was not deleted'.format(tmp_path))
|
||||
|
||||
def test_art_file_missing(self):
|
||||
self.add_album_fixture()
|
||||
|
|
@ -114,7 +113,7 @@ class EmbedartCliTest(_common.TestCase, TestHelper):
|
|||
logging.getLogger('beets.embedart').setLevel(logging.DEBUG)
|
||||
|
||||
handle, tmp_path = tempfile.mkstemp()
|
||||
os.write(handle, 'I am not an image.')
|
||||
os.write(handle, u'I am not an image.')
|
||||
os.close(handle)
|
||||
|
||||
try:
|
||||
|
|
@ -136,7 +135,7 @@ class EmbedartCliTest(_common.TestCase, TestHelper):
|
|||
mediafile = MediaFile(syspath(item.path))
|
||||
|
||||
self.assertEqual(mediafile.images[0].data, self.image_data,
|
||||
'Image written is not {0}'.format(
|
||||
u'Image written is not {0}'.format(
|
||||
self.abbey_artpath))
|
||||
|
||||
@require_artresizer_compare
|
||||
|
|
@ -150,7 +149,7 @@ class EmbedartCliTest(_common.TestCase, TestHelper):
|
|||
mediafile = MediaFile(syspath(item.path))
|
||||
|
||||
self.assertEqual(mediafile.images[0].data, self.image_data,
|
||||
'Image written is not {0}'.format(
|
||||
u'Image written is not {0}'.format(
|
||||
self.abbey_similarpath))
|
||||
|
||||
def test_non_ascii_album_path(self):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from test._common import unittest
|
||||
from test.helper import TestHelper
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
from test._common import unittest
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Test file manipulation functionality of Item.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import shutil
|
||||
import os
|
||||
|
|
@ -73,7 +72,7 @@ class MoveTest(_common.TestCase):
|
|||
old_path = self.i.path
|
||||
self.assertExists(old_path)
|
||||
|
||||
self.i.artist = 'newArtist'
|
||||
self.i.artist = u'newArtist'
|
||||
self.i.move()
|
||||
self.assertNotExists(old_path)
|
||||
self.assertNotExists(os.path.dirname(old_path))
|
||||
|
|
@ -196,7 +195,7 @@ class AlbumFileTest(_common.TestCase):
|
|||
self.otherdir = os.path.join(self.temp_dir, 'testotherdir')
|
||||
|
||||
def test_albuminfo_move_changes_paths(self):
|
||||
self.ai.album = 'newAlbumName'
|
||||
self.ai.album = u'newAlbumName'
|
||||
self.ai.move()
|
||||
self.ai.store()
|
||||
self.i.load()
|
||||
|
|
@ -205,7 +204,7 @@ class AlbumFileTest(_common.TestCase):
|
|||
|
||||
def test_albuminfo_move_moves_file(self):
|
||||
oldpath = self.i.path
|
||||
self.ai.album = 'newAlbumName'
|
||||
self.ai.album = u'newAlbumName'
|
||||
self.ai.move()
|
||||
self.ai.store()
|
||||
self.i.load()
|
||||
|
|
@ -215,7 +214,7 @@ class AlbumFileTest(_common.TestCase):
|
|||
|
||||
def test_albuminfo_move_copies_file(self):
|
||||
oldpath = self.i.path
|
||||
self.ai.album = 'newAlbumName'
|
||||
self.ai.album = u'newAlbumName'
|
||||
self.ai.move(True)
|
||||
self.ai.store()
|
||||
self.i.load()
|
||||
|
|
@ -261,7 +260,7 @@ class ArtFileTest(_common.TestCase):
|
|||
def test_art_moves_with_album(self):
|
||||
self.assertTrue(os.path.exists(self.art))
|
||||
oldpath = self.i.path
|
||||
self.ai.album = 'newAlbum'
|
||||
self.ai.album = u'newAlbum'
|
||||
self.ai.move()
|
||||
self.i.load()
|
||||
|
||||
|
|
@ -289,7 +288,7 @@ class ArtFileTest(_common.TestCase):
|
|||
touch(newart)
|
||||
i2 = item()
|
||||
i2.path = self.i.path
|
||||
i2.artist = 'someArtist'
|
||||
i2.artist = u'someArtist'
|
||||
ai = self.lib.add_album((i2,))
|
||||
i2.move(True)
|
||||
|
||||
|
|
@ -305,7 +304,7 @@ class ArtFileTest(_common.TestCase):
|
|||
touch(newart)
|
||||
i2 = item()
|
||||
i2.path = self.i.path
|
||||
i2.artist = 'someArtist'
|
||||
i2.artist = u'someArtist'
|
||||
ai = self.lib.add_album((i2,))
|
||||
i2.move(True)
|
||||
ai.set_art(newart)
|
||||
|
|
@ -319,7 +318,7 @@ class ArtFileTest(_common.TestCase):
|
|||
touch(newart)
|
||||
i2 = item()
|
||||
i2.path = self.i.path
|
||||
i2.artist = 'someArtist'
|
||||
i2.artist = u'someArtist'
|
||||
ai = self.lib.add_album((i2,))
|
||||
i2.move(True)
|
||||
|
||||
|
|
@ -336,7 +335,7 @@ class ArtFileTest(_common.TestCase):
|
|||
touch(newart)
|
||||
i2 = item()
|
||||
i2.path = self.i.path
|
||||
i2.artist = 'someArtist'
|
||||
i2.artist = u'someArtist'
|
||||
ai = self.lib.add_album((i2,))
|
||||
i2.move(True)
|
||||
|
||||
|
|
@ -360,7 +359,7 @@ class ArtFileTest(_common.TestCase):
|
|||
try:
|
||||
i2 = item()
|
||||
i2.path = self.i.path
|
||||
i2.artist = 'someArtist'
|
||||
i2.artist = u'someArtist'
|
||||
ai = self.lib.add_album((i2,))
|
||||
i2.move(True)
|
||||
ai.set_art(newart)
|
||||
|
|
@ -378,12 +377,12 @@ class ArtFileTest(_common.TestCase):
|
|||
oldartpath = self.lib.albums()[0].artpath
|
||||
self.assertExists(oldartpath)
|
||||
|
||||
self.ai.album = 'different_album'
|
||||
self.ai.album = u'different_album'
|
||||
self.ai.store()
|
||||
self.ai.items()[0].move()
|
||||
|
||||
artpath = self.lib.albums()[0].artpath
|
||||
self.assertTrue('different_album' in artpath)
|
||||
self.assertTrue(u'different_album' in artpath)
|
||||
self.assertExists(artpath)
|
||||
self.assertNotExists(oldartpath)
|
||||
|
||||
|
|
@ -395,12 +394,12 @@ class ArtFileTest(_common.TestCase):
|
|||
oldartpath = self.lib.albums()[0].artpath
|
||||
self.assertExists(oldartpath)
|
||||
|
||||
self.i.album = 'different_album'
|
||||
self.i.album = u'different_album'
|
||||
self.i.album_id = None # detach from album
|
||||
self.i.move()
|
||||
|
||||
artpath = self.lib.albums()[0].artpath
|
||||
self.assertFalse('different_album' in artpath)
|
||||
self.assertFalse(u'different_album' in artpath)
|
||||
self.assertEqual(artpath, oldartpath)
|
||||
self.assertExists(oldartpath)
|
||||
|
||||
|
|
@ -481,7 +480,7 @@ class SoftRemoveTest(_common.TestCase):
|
|||
try:
|
||||
util.remove(self.path + 'XXX', True)
|
||||
except OSError:
|
||||
self.fail('OSError when removing path')
|
||||
self.fail(u'OSError when removing path')
|
||||
|
||||
|
||||
class SafeMoveCopyTest(_common.TestCase):
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Tests for the 'ftintitle' plugin."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from test._common import unittest
|
||||
from test.helper import TestHelper
|
||||
|
|
@ -150,33 +149,33 @@ class FtInTitlePluginTest(unittest.TestCase):
|
|||
self.assertEqual(feat_part, test_case['feat_part'])
|
||||
|
||||
def test_split_on_feat(self):
|
||||
parts = ftintitle.split_on_feat('Alice ft. Bob')
|
||||
self.assertEqual(parts, ('Alice', 'Bob'))
|
||||
parts = ftintitle.split_on_feat('Alice feat Bob')
|
||||
self.assertEqual(parts, ('Alice', 'Bob'))
|
||||
parts = ftintitle.split_on_feat('Alice feat. Bob')
|
||||
self.assertEqual(parts, ('Alice', 'Bob'))
|
||||
parts = ftintitle.split_on_feat('Alice featuring Bob')
|
||||
self.assertEqual(parts, ('Alice', 'Bob'))
|
||||
parts = ftintitle.split_on_feat('Alice & Bob')
|
||||
self.assertEqual(parts, ('Alice', 'Bob'))
|
||||
parts = ftintitle.split_on_feat('Alice and Bob')
|
||||
self.assertEqual(parts, ('Alice', 'Bob'))
|
||||
parts = ftintitle.split_on_feat('Alice With Bob')
|
||||
self.assertEqual(parts, ('Alice', 'Bob'))
|
||||
parts = ftintitle.split_on_feat('Alice defeat Bob')
|
||||
self.assertEqual(parts, ('Alice defeat Bob', None))
|
||||
parts = ftintitle.split_on_feat(u'Alice ft. Bob')
|
||||
self.assertEqual(parts, (u'Alice', u'Bob'))
|
||||
parts = ftintitle.split_on_feat(u'Alice feat Bob')
|
||||
self.assertEqual(parts, (u'Alice', u'Bob'))
|
||||
parts = ftintitle.split_on_feat(u'Alice feat. Bob')
|
||||
self.assertEqual(parts, (u'Alice', u'Bob'))
|
||||
parts = ftintitle.split_on_feat(u'Alice featuring Bob')
|
||||
self.assertEqual(parts, (u'Alice', u'Bob'))
|
||||
parts = ftintitle.split_on_feat(u'Alice & Bob')
|
||||
self.assertEqual(parts, (u'Alice', u'Bob'))
|
||||
parts = ftintitle.split_on_feat(u'Alice and Bob')
|
||||
self.assertEqual(parts, (u'Alice', u'Bob'))
|
||||
parts = ftintitle.split_on_feat(u'Alice With Bob')
|
||||
self.assertEqual(parts, (u'Alice', u'Bob'))
|
||||
parts = ftintitle.split_on_feat(u'Alice defeat Bob')
|
||||
self.assertEqual(parts, (u'Alice defeat Bob', None))
|
||||
|
||||
def test_contains_feat(self):
|
||||
self.assertTrue(ftintitle.contains_feat('Alice ft. Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat('Alice feat. Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat('Alice feat Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat('Alice featuring Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat('Alice & Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat('Alice and Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat('Alice With Bob'))
|
||||
self.assertFalse(ftintitle.contains_feat('Alice defeat Bob'))
|
||||
self.assertFalse(ftintitle.contains_feat('Aliceft.Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat(u'Alice ft. Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat(u'Alice feat. Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat(u'Alice feat Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat(u'Alice featuring Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat(u'Alice & Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat(u'Alice and Bob'))
|
||||
self.assertTrue(ftintitle.contains_feat(u'Alice With Bob'))
|
||||
self.assertFalse(ftintitle.contains_feat(u'Alice defeat Bob'))
|
||||
self.assertFalse(ftintitle.contains_feat(u'Aliceft.Bob'))
|
||||
|
||||
|
||||
def suite():
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
"""Tests for the 'ihate' plugin"""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from test._common import unittest
|
||||
from beets import importer
|
||||
|
|
@ -17,7 +16,7 @@ class IHatePluginTest(unittest.TestCase):
|
|||
|
||||
match_pattern = {}
|
||||
test_item = Item(
|
||||
genre='TestGenre',
|
||||
genre=u'TestGenre',
|
||||
album=u'TestAlbum',
|
||||
artist=u'TestArtist')
|
||||
task = importer.SingletonImportTask(None, test_item)
|
||||
|
|
@ -26,25 +25,25 @@ class IHatePluginTest(unittest.TestCase):
|
|||
self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
|
||||
|
||||
# 1 query match.
|
||||
match_pattern = ["artist:bad_artist", "artist:TestArtist"]
|
||||
match_pattern = [u"artist:bad_artist", u"artist:TestArtist"]
|
||||
self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
|
||||
|
||||
# 2 query matches, either should trigger.
|
||||
match_pattern = ["album:test", "artist:testartist"]
|
||||
match_pattern = [u"album:test", u"artist:testartist"]
|
||||
self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
|
||||
|
||||
# Query is blocked by AND clause.
|
||||
match_pattern = ["album:notthis genre:testgenre"]
|
||||
match_pattern = [u"album:notthis genre:testgenre"]
|
||||
self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
|
||||
|
||||
# Both queries are blocked by AND clause with unmatched condition.
|
||||
match_pattern = ["album:notthis genre:testgenre",
|
||||
"artist:testartist album:notthis"]
|
||||
match_pattern = [u"album:notthis genre:testgenre",
|
||||
u"artist:testartist album:notthis"]
|
||||
self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
|
||||
|
||||
# Only one query should fire.
|
||||
match_pattern = ["album:testalbum genre:testgenre",
|
||||
"artist:testartist album:notthis"]
|
||||
match_pattern = [u"album:testalbum genre:testgenre",
|
||||
u"artist:testartist album:notthis"]
|
||||
self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
"""Tests for the `importadded` plugin."""
|
||||
|
||||
|
|
@ -73,7 +72,7 @@ class ImportAddedTest(unittest.TestCase, ImportHelper):
|
|||
for m in self.media_files:
|
||||
if m.title.replace('Tag', 'Applied') == item.title:
|
||||
return m
|
||||
raise AssertionError("No MediaFile found for Item " +
|
||||
raise AssertionError(u"No MediaFile found for Item " +
|
||||
util.displayable_path(item.path))
|
||||
|
||||
def assertEqualTimes(self, first, second, msg=None):
|
||||
|
|
@ -127,7 +126,7 @@ class ImportAddedTest(unittest.TestCase, ImportHelper):
|
|||
for item in album.items())
|
||||
for item_path, added_after in items_added_after.iteritems():
|
||||
self.assertEqualTimes(items_added_before[item_path], added_after,
|
||||
"reimport modified Item.added for " +
|
||||
u"reimport modified Item.added for " +
|
||||
item_path)
|
||||
|
||||
def test_import_singletons_with_added_dates(self):
|
||||
|
|
@ -165,7 +164,7 @@ class ImportAddedTest(unittest.TestCase, ImportHelper):
|
|||
for item in self.lib.items())
|
||||
for item_path, added_after in items_added_after.iteritems():
|
||||
self.assertEqualTimes(items_added_before[item_path], added_after,
|
||||
"reimport modified Item.added for " +
|
||||
u"reimport modified Item.added for " +
|
||||
item_path)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
"""Tests for the general importer functionality.
|
||||
"""
|
||||
|
|
@ -121,7 +120,7 @@ class AutotagStub(object):
|
|||
else:
|
||||
id = ''
|
||||
if artist is None:
|
||||
artist = "Various Artists"
|
||||
artist = u"Various Artists"
|
||||
else:
|
||||
artist = artist.replace('Tag', 'Applied') + id
|
||||
album = album.replace('Tag', 'Applied') + id
|
||||
|
|
@ -150,9 +149,9 @@ class ImportHelper(TestHelper):
|
|||
def setup_beets(self, disk=False):
|
||||
super(ImportHelper, self).setup_beets(disk)
|
||||
self.lib.path_formats = [
|
||||
('default', os.path.join('$artist', '$album', '$title')),
|
||||
('singleton:true', os.path.join('singletons', '$title')),
|
||||
('comp:true', os.path.join('compilations', '$album', '$title')),
|
||||
(u'default', os.path.join('$artist', '$album', '$title')),
|
||||
(u'singleton:true', os.path.join('singletons', '$title')),
|
||||
(u'comp:true', os.path.join('compilations', '$album', '$title')),
|
||||
]
|
||||
|
||||
def _create_import_dir(self, count=3):
|
||||
|
|
@ -179,8 +178,8 @@ class ImportHelper(TestHelper):
|
|||
resource_path = os.path.join(_common.RSRC, 'full.mp3')
|
||||
|
||||
metadata = {
|
||||
'artist': 'Tag Artist',
|
||||
'album': 'Tag Album',
|
||||
'artist': u'Tag Artist',
|
||||
'album': u'Tag Album',
|
||||
'albumartist': None,
|
||||
'mb_trackid': None,
|
||||
'mb_albumid': None,
|
||||
|
|
@ -195,7 +194,7 @@ class ImportHelper(TestHelper):
|
|||
|
||||
# Set metadata
|
||||
metadata['track'] = i + 1
|
||||
metadata['title'] = 'Tag Title %d' % (i + 1)
|
||||
metadata['title'] = u'Tag Title %d' % (i + 1)
|
||||
for attr in metadata:
|
||||
setattr(medium, attr, metadata[attr])
|
||||
medium.save()
|
||||
|
|
@ -250,7 +249,7 @@ class NonAutotaggedImportTest(_common.TestCase, ImportHelper):
|
|||
self.importer.run()
|
||||
albums = self.lib.albums()
|
||||
self.assertEqual(len(albums), 1)
|
||||
self.assertEqual(albums[0].albumartist, 'Tag Artist')
|
||||
self.assertEqual(albums[0].albumartist, u'Tag Artist')
|
||||
|
||||
def test_import_copy_arrives(self):
|
||||
self.importer.run()
|
||||
|
|
@ -411,7 +410,7 @@ class ImportTarTest(ImportZipTest):
|
|||
return path
|
||||
|
||||
|
||||
@unittest.skipIf(not has_program('unrar'), 'unrar program not found')
|
||||
@unittest.skipIf(not has_program('unrar'), u'unrar program not found')
|
||||
class ImportRarTest(ImportZipTest):
|
||||
|
||||
def create_archive(self):
|
||||
|
|
@ -446,7 +445,7 @@ class ImportSingletonTest(_common.TestCase, ImportHelper):
|
|||
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.items().get().title, 'Tag Title 1')
|
||||
self.assertEqual(self.lib.items().get().title, u'Tag Title 1')
|
||||
|
||||
def test_apply_asis_does_not_add_album(self):
|
||||
self.assertEqual(self.lib.albums().get(), None)
|
||||
|
|
@ -467,7 +466,7 @@ class ImportSingletonTest(_common.TestCase, ImportHelper):
|
|||
|
||||
self.importer.add_choice(importer.action.APPLY)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.items().get().title, 'Applied Title 1')
|
||||
self.assertEqual(self.lib.items().get().title, u'Applied Title 1')
|
||||
|
||||
def test_apply_candidate_does_not_add_album(self):
|
||||
self.importer.add_choice(importer.action.APPLY)
|
||||
|
|
@ -479,7 +478,7 @@ class ImportSingletonTest(_common.TestCase, ImportHelper):
|
|||
|
||||
self.importer.add_choice(importer.action.APPLY)
|
||||
self.importer.run()
|
||||
self.assert_file_in_lib('singletons', 'Applied Title 1.mp3')
|
||||
self.assert_file_in_lib('singletons', u'Applied Title 1.mp3')
|
||||
|
||||
def test_skip_does_not_add_first_track(self):
|
||||
self.importer.add_choice(importer.action.SKIP)
|
||||
|
|
@ -532,13 +531,13 @@ class ImportTest(_common.TestCase, ImportHelper):
|
|||
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().album, 'Tag Album')
|
||||
self.assertEqual(self.lib.albums().get().album, u'Tag Album')
|
||||
|
||||
def test_apply_asis_adds_tracks(self):
|
||||
self.assertEqual(self.lib.items().get(), None)
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.items().get().title, 'Tag Title 1')
|
||||
self.assertEqual(self.lib.items().get().title, u'Tag Title 1')
|
||||
|
||||
def test_apply_asis_adds_album_path(self):
|
||||
self.assert_lib_dir_empty()
|
||||
|
|
@ -552,14 +551,14 @@ class ImportTest(_common.TestCase, ImportHelper):
|
|||
|
||||
self.importer.add_choice(importer.action.APPLY)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().album, 'Applied Album')
|
||||
self.assertEqual(self.lib.albums().get().album, u'Applied Album')
|
||||
|
||||
def test_apply_candidate_adds_tracks(self):
|
||||
self.assertEqual(self.lib.items().get(), None)
|
||||
|
||||
self.importer.add_choice(importer.action.APPLY)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.items().get().title, 'Applied Title 1')
|
||||
self.assertEqual(self.lib.items().get().title, u'Applied Title 1')
|
||||
|
||||
def test_apply_candidate_adds_album_path(self):
|
||||
self.assert_lib_dir_empty()
|
||||
|
|
@ -617,7 +616,7 @@ class ImportTest(_common.TestCase, ImportHelper):
|
|||
with capture_log() as logs:
|
||||
self.importer.run()
|
||||
|
||||
self.assertIn('No files imported from {0}'.format(import_dir), logs)
|
||||
self.assertIn(u'No files imported from {0}'.format(import_dir), logs)
|
||||
|
||||
def test_empty_directory_singleton_warning(self):
|
||||
import_dir = os.path.join(self.temp_dir, 'empty')
|
||||
|
|
@ -626,7 +625,7 @@ class ImportTest(_common.TestCase, ImportHelper):
|
|||
with capture_log() as logs:
|
||||
self.importer.run()
|
||||
|
||||
self.assertIn('No files imported from {0}'.format(import_dir), logs)
|
||||
self.assertIn(u'No files imported from {0}'.format(import_dir), logs)
|
||||
|
||||
def test_asis_no_data_source(self):
|
||||
self.assertEqual(self.lib.items().get(), None)
|
||||
|
|
@ -659,7 +658,7 @@ class ImportTracksTest(_common.TestCase, ImportHelper):
|
|||
self.importer.add_choice(importer.action.APPLY)
|
||||
self.importer.add_choice(importer.action.APPLY)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.items().get().title, 'Applied Title 1')
|
||||
self.assertEqual(self.lib.items().get().title, u'Applied Title 1')
|
||||
self.assertEqual(self.lib.albums().get(), None)
|
||||
|
||||
def test_apply_tracks_adds_singleton_path(self):
|
||||
|
|
@ -688,27 +687,27 @@ class ImportCompilationTest(_common.TestCase, ImportHelper):
|
|||
def test_asis_homogenous_sets_albumartist(self):
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().albumartist, 'Tag Artist')
|
||||
self.assertEqual(self.lib.albums().get().albumartist, u'Tag Artist')
|
||||
for item in self.lib.items():
|
||||
self.assertEqual(item.albumartist, 'Tag Artist')
|
||||
self.assertEqual(item.albumartist, u'Tag Artist')
|
||||
|
||||
def test_asis_heterogenous_sets_various_albumartist(self):
|
||||
self.import_media[0].artist = 'Other Artist'
|
||||
self.import_media[0].artist = u'Other Artist'
|
||||
self.import_media[0].save()
|
||||
self.import_media[1].artist = 'Another Artist'
|
||||
self.import_media[1].artist = u'Another Artist'
|
||||
self.import_media[1].save()
|
||||
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().albumartist,
|
||||
'Various Artists')
|
||||
u'Various Artists')
|
||||
for item in self.lib.items():
|
||||
self.assertEqual(item.albumartist, 'Various Artists')
|
||||
self.assertEqual(item.albumartist, u'Various Artists')
|
||||
|
||||
def test_asis_heterogenous_sets_sompilation(self):
|
||||
self.import_media[0].artist = 'Other Artist'
|
||||
self.import_media[0].artist = u'Other Artist'
|
||||
self.import_media[0].save()
|
||||
self.import_media[1].artist = 'Another Artist'
|
||||
self.import_media[1].artist = u'Another Artist'
|
||||
self.import_media[1].save()
|
||||
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
|
|
@ -717,33 +716,33 @@ class ImportCompilationTest(_common.TestCase, ImportHelper):
|
|||
self.assertTrue(item.comp)
|
||||
|
||||
def test_asis_sets_majority_albumartist(self):
|
||||
self.import_media[0].artist = 'Other Artist'
|
||||
self.import_media[0].artist = u'Other Artist'
|
||||
self.import_media[0].save()
|
||||
self.import_media[1].artist = 'Other Artist'
|
||||
self.import_media[1].artist = u'Other Artist'
|
||||
self.import_media[1].save()
|
||||
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().albumartist, 'Other Artist')
|
||||
self.assertEqual(self.lib.albums().get().albumartist, u'Other Artist')
|
||||
for item in self.lib.items():
|
||||
self.assertEqual(item.albumartist, 'Other Artist')
|
||||
self.assertEqual(item.albumartist, u'Other Artist')
|
||||
|
||||
def test_asis_albumartist_tag_sets_albumartist(self):
|
||||
self.import_media[0].artist = 'Other Artist'
|
||||
self.import_media[1].artist = 'Another Artist'
|
||||
self.import_media[0].artist = u'Other Artist'
|
||||
self.import_media[1].artist = u'Another Artist'
|
||||
for mediafile in self.import_media:
|
||||
mediafile.albumartist = 'Album Artist'
|
||||
mediafile.mb_albumartistid = 'Album Artist ID'
|
||||
mediafile.albumartist = u'Album Artist'
|
||||
mediafile.mb_albumartistid = u'Album Artist ID'
|
||||
mediafile.save()
|
||||
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().albumartist, 'Album Artist')
|
||||
self.assertEqual(self.lib.albums().get().albumartist, u'Album Artist')
|
||||
self.assertEqual(self.lib.albums().get().mb_albumartistid,
|
||||
'Album Artist ID')
|
||||
u'Album Artist ID')
|
||||
for item in self.lib.items():
|
||||
self.assertEqual(item.albumartist, 'Album Artist')
|
||||
self.assertEqual(item.mb_albumartistid, 'Album Artist ID')
|
||||
self.assertEqual(item.albumartist, u'Album Artist')
|
||||
self.assertEqual(item.mb_albumartistid, u'Album Artist ID')
|
||||
|
||||
|
||||
class ImportExistingTest(_common.TestCase, ImportHelper):
|
||||
|
|
@ -794,17 +793,17 @@ class ImportExistingTest(_common.TestCase, ImportHelper):
|
|||
def test_asis_updates_metadata(self):
|
||||
self.setup_importer.run()
|
||||
medium = MediaFile(self.lib.items().get().path)
|
||||
medium.title = 'New Title'
|
||||
medium.title = u'New Title'
|
||||
medium.save()
|
||||
|
||||
self.importer.add_choice(importer.action.ASIS)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.items().get().title, 'New Title')
|
||||
self.assertEqual(self.lib.items().get().title, u'New Title')
|
||||
|
||||
def test_asis_updated_moves_file(self):
|
||||
self.setup_importer.run()
|
||||
medium = MediaFile(self.lib.items().get().path)
|
||||
medium.title = 'New Title'
|
||||
medium.title = u'New Title'
|
||||
medium.save()
|
||||
|
||||
old_path = os.path.join('Applied Artist', 'Applied Album',
|
||||
|
|
@ -820,7 +819,7 @@ class ImportExistingTest(_common.TestCase, ImportHelper):
|
|||
def test_asis_updated_without_copy_does_not_move_file(self):
|
||||
self.setup_importer.run()
|
||||
medium = MediaFile(self.lib.items().get().path)
|
||||
medium.title = 'New Title'
|
||||
medium.title = u'New Title'
|
||||
medium.save()
|
||||
|
||||
old_path = os.path.join('Applied Artist', 'Applied Album',
|
||||
|
|
@ -881,8 +880,8 @@ class GroupAlbumsImportTest(_common.TestCase, ImportHelper):
|
|||
self.matcher.restore()
|
||||
|
||||
def test_add_album_for_different_artist_and_different_album(self):
|
||||
self.import_media[0].artist = "Artist B"
|
||||
self.import_media[0].album = "Album B"
|
||||
self.import_media[0].artist = u"Artist B"
|
||||
self.import_media[0].album = u"Album B"
|
||||
self.import_media[0].save()
|
||||
|
||||
self.importer.run()
|
||||
|
|
@ -890,11 +889,11 @@ class GroupAlbumsImportTest(_common.TestCase, ImportHelper):
|
|||
self.assertEqual(albums, set(['Album B', 'Tag Album']))
|
||||
|
||||
def test_add_album_for_different_artist_and_same_albumartist(self):
|
||||
self.import_media[0].artist = "Artist B"
|
||||
self.import_media[0].albumartist = "Album Artist"
|
||||
self.import_media[0].artist = u"Artist B"
|
||||
self.import_media[0].albumartist = u"Album Artist"
|
||||
self.import_media[0].save()
|
||||
self.import_media[1].artist = "Artist C"
|
||||
self.import_media[1].albumartist = "Album Artist"
|
||||
self.import_media[1].artist = u"Artist C"
|
||||
self.import_media[1].albumartist = u"Album Artist"
|
||||
self.import_media[1].save()
|
||||
|
||||
self.importer.run()
|
||||
|
|
@ -902,7 +901,7 @@ class GroupAlbumsImportTest(_common.TestCase, ImportHelper):
|
|||
self.assertEqual(artists, set(['Album Artist', 'Tag Artist']))
|
||||
|
||||
def test_add_album_for_same_artist_and_different_album(self):
|
||||
self.import_media[0].album = "Album B"
|
||||
self.import_media[0].album = u"Album B"
|
||||
self.import_media[0].save()
|
||||
|
||||
self.importer.run()
|
||||
|
|
@ -910,7 +909,7 @@ class GroupAlbumsImportTest(_common.TestCase, ImportHelper):
|
|||
self.assertEqual(albums, set(['Album B', 'Tag Album']))
|
||||
|
||||
def test_add_album_for_same_album_and_different_artist(self):
|
||||
self.import_media[0].artist = "Artist B"
|
||||
self.import_media[0].artist = u"Artist B"
|
||||
self.import_media[0].save()
|
||||
|
||||
self.importer.run()
|
||||
|
|
@ -919,7 +918,7 @@ class GroupAlbumsImportTest(_common.TestCase, ImportHelper):
|
|||
|
||||
def test_incremental(self):
|
||||
config['import']['incremental'] = True
|
||||
self.import_media[0].album = "Album B"
|
||||
self.import_media[0].album = u"Album B"
|
||||
self.import_media[0].save()
|
||||
|
||||
self.importer.run()
|
||||
|
|
@ -951,12 +950,12 @@ class ChooseCandidateTest(_common.TestCase, ImportHelper):
|
|||
def test_choose_first_candidate(self):
|
||||
self.importer.add_choice(1)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().album, 'Applied Album M')
|
||||
self.assertEqual(self.lib.albums().get().album, u'Applied Album M')
|
||||
|
||||
def test_choose_second_candidate(self):
|
||||
self.importer.add_choice(2)
|
||||
self.importer.run()
|
||||
self.assertEqual(self.lib.albums().get().album, 'Applied Album MM')
|
||||
self.assertEqual(self.lib.albums().get().album, u'Applied Album MM')
|
||||
|
||||
|
||||
class InferAlbumDataTest(_common.TestCase):
|
||||
|
|
@ -966,9 +965,9 @@ class InferAlbumDataTest(_common.TestCase):
|
|||
i1 = _common.item()
|
||||
i2 = _common.item()
|
||||
i3 = _common.item()
|
||||
i1.title = 'first item'
|
||||
i2.title = 'second item'
|
||||
i3.title = 'third item'
|
||||
i1.title = u'first item'
|
||||
i2.title = u'second item'
|
||||
i3.title = u'third item'
|
||||
i1.comp = i2.comp = i3.comp = False
|
||||
i1.albumartist = i2.albumartist = i3.albumartist = ''
|
||||
i1.mb_albumartistid = i2.mb_albumartistid = i3.mb_albumartistid = ''
|
||||
|
|
@ -984,28 +983,28 @@ class InferAlbumDataTest(_common.TestCase):
|
|||
self.assertEqual(self.items[0].albumartist, self.items[2].artist)
|
||||
|
||||
def test_asis_heterogenous_va(self):
|
||||
self.items[0].artist = 'another artist'
|
||||
self.items[1].artist = 'some other artist'
|
||||
self.items[0].artist = u'another artist'
|
||||
self.items[1].artist = u'some other artist'
|
||||
self.task.set_choice(importer.action.ASIS)
|
||||
|
||||
self.task.align_album_level_fields()
|
||||
|
||||
self.assertTrue(self.items[0].comp)
|
||||
self.assertEqual(self.items[0].albumartist, 'Various Artists')
|
||||
self.assertEqual(self.items[0].albumartist, u'Various Artists')
|
||||
|
||||
def test_asis_comp_applied_to_all_items(self):
|
||||
self.items[0].artist = 'another artist'
|
||||
self.items[1].artist = 'some other artist'
|
||||
self.items[0].artist = u'another artist'
|
||||
self.items[1].artist = u'some other artist'
|
||||
self.task.set_choice(importer.action.ASIS)
|
||||
|
||||
self.task.align_album_level_fields()
|
||||
|
||||
for item in self.items:
|
||||
self.assertTrue(item.comp)
|
||||
self.assertEqual(item.albumartist, 'Various Artists')
|
||||
self.assertEqual(item.albumartist, u'Various Artists')
|
||||
|
||||
def test_asis_majority_artist_single_artist(self):
|
||||
self.items[0].artist = 'another artist'
|
||||
self.items[0].artist = u'another artist'
|
||||
self.task.set_choice(importer.action.ASIS)
|
||||
|
||||
self.task.align_album_level_fields()
|
||||
|
|
@ -1014,19 +1013,19 @@ class InferAlbumDataTest(_common.TestCase):
|
|||
self.assertEqual(self.items[0].albumartist, self.items[2].artist)
|
||||
|
||||
def test_asis_track_albumartist_override(self):
|
||||
self.items[0].artist = 'another artist'
|
||||
self.items[1].artist = 'some other artist'
|
||||
self.items[0].artist = u'another artist'
|
||||
self.items[1].artist = u'some other artist'
|
||||
for item in self.items:
|
||||
item.albumartist = 'some album artist'
|
||||
item.mb_albumartistid = 'some album artist id'
|
||||
item.albumartist = u'some album artist'
|
||||
item.mb_albumartistid = u'some album artist id'
|
||||
self.task.set_choice(importer.action.ASIS)
|
||||
|
||||
self.task.align_album_level_fields()
|
||||
|
||||
self.assertEqual(self.items[0].albumartist,
|
||||
'some album artist')
|
||||
u'some album artist')
|
||||
self.assertEqual(self.items[0].mb_albumartistid,
|
||||
'some album artist id')
|
||||
u'some album artist id')
|
||||
|
||||
def test_apply_gets_artist_and_id(self):
|
||||
self.task.set_choice(AlbumMatch(0, None, {}, set(), set())) # APPLY
|
||||
|
|
@ -1039,16 +1038,16 @@ class InferAlbumDataTest(_common.TestCase):
|
|||
|
||||
def test_apply_lets_album_values_override(self):
|
||||
for item in self.items:
|
||||
item.albumartist = 'some album artist'
|
||||
item.mb_albumartistid = 'some album artist id'
|
||||
item.albumartist = u'some album artist'
|
||||
item.mb_albumartistid = u'some album artist id'
|
||||
self.task.set_choice(AlbumMatch(0, None, {}, set(), set())) # APPLY
|
||||
|
||||
self.task.align_album_level_fields()
|
||||
|
||||
self.assertEqual(self.items[0].albumartist,
|
||||
'some album artist')
|
||||
u'some album artist')
|
||||
self.assertEqual(self.items[0].mb_albumartistid,
|
||||
'some album artist id')
|
||||
u'some album artist id')
|
||||
|
||||
def test_small_single_artist_album(self):
|
||||
self.items = [self.items[0]]
|
||||
|
|
@ -1272,11 +1271,11 @@ class ResumeImportTest(unittest.TestCase, TestHelper):
|
|||
|
||||
self.importer.run()
|
||||
self.assertEqual(len(self.lib.albums()), 1)
|
||||
self.assertIsNotNone(self.lib.albums('album:album 0').get())
|
||||
self.assertIsNotNone(self.lib.albums(u'album:album 0').get())
|
||||
|
||||
self.importer.run()
|
||||
self.assertEqual(len(self.lib.albums()), 2)
|
||||
self.assertIsNotNone(self.lib.albums('album:album 1').get())
|
||||
self.assertIsNotNone(self.lib.albums(u'album:album 1').get())
|
||||
|
||||
@patch('beets.plugins.send')
|
||||
def test_resume_singleton(self, plugins_send):
|
||||
|
|
@ -1293,11 +1292,11 @@ class ResumeImportTest(unittest.TestCase, TestHelper):
|
|||
|
||||
self.importer.run()
|
||||
self.assertEqual(len(self.lib.items()), 1)
|
||||
self.assertIsNotNone(self.lib.items('title:track 0').get())
|
||||
self.assertIsNotNone(self.lib.items(u'title:track 0').get())
|
||||
|
||||
self.importer.run()
|
||||
self.assertEqual(len(self.lib.items()), 2)
|
||||
self.assertIsNotNone(self.lib.items('title:track 1').get())
|
||||
self.assertIsNotNone(self.lib.items(u'title:track 1').get())
|
||||
|
||||
|
||||
class IncrementalImportTest(unittest.TestCase, TestHelper):
|
||||
|
|
@ -1685,7 +1684,7 @@ class ImportPretendTest(_common.TestCase, ImportHelper):
|
|||
def test_import_pretend_empty(self):
|
||||
logs = self.__run([self.empty_path])
|
||||
|
||||
self.assertEqual(logs, ['No files imported from {0}'
|
||||
self.assertEqual(logs, [u'No files imported from {0}'
|
||||
.format(displayable_path(self.empty_path))])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from test._common import unittest
|
||||
from test.helper import TestHelper
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from mock import patch
|
||||
from test._common import unittest
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Tests for the 'lastgenre' plugin."""
|
||||
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from mock import Mock
|
||||
|
||||
|
|
@ -52,7 +51,7 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
"""
|
||||
self._setup_config()
|
||||
self.assertEqual(self.plugin._resolve_genres(['delta blues']),
|
||||
'Delta Blues')
|
||||
u'Delta Blues')
|
||||
|
||||
def test_c14n_only(self):
|
||||
"""Default c14n tree funnels up to most common genre except for *wrong*
|
||||
|
|
@ -60,16 +59,16 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
"""
|
||||
self._setup_config(canonical=True, count=99)
|
||||
self.assertEqual(self.plugin._resolve_genres(['delta blues']),
|
||||
'Blues')
|
||||
u'Blues')
|
||||
self.assertEqual(self.plugin._resolve_genres(['iota blues']),
|
||||
'Iota Blues')
|
||||
u'Iota Blues')
|
||||
|
||||
def test_whitelist_only(self):
|
||||
"""Default whitelist rejects *wrong* (non existing) genres.
|
||||
"""
|
||||
self._setup_config(whitelist=True)
|
||||
self.assertEqual(self.plugin._resolve_genres(['iota blues']),
|
||||
'')
|
||||
u'')
|
||||
|
||||
def test_whitelist_c14n(self):
|
||||
"""Default whitelist and c14n both activated result in all parents
|
||||
|
|
@ -77,7 +76,7 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
"""
|
||||
self._setup_config(canonical=True, whitelist=True, count=99)
|
||||
self.assertEqual(self.plugin._resolve_genres(['delta blues']),
|
||||
'Delta Blues, Blues')
|
||||
u'Delta Blues, Blues')
|
||||
|
||||
def test_whitelist_custom(self):
|
||||
"""Keep only genres that are in the whitelist.
|
||||
|
|
@ -85,11 +84,11 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
self._setup_config(whitelist=set(['blues', 'rock', 'jazz']),
|
||||
count=2)
|
||||
self.assertEqual(self.plugin._resolve_genres(['pop', 'blues']),
|
||||
'Blues')
|
||||
u'Blues')
|
||||
|
||||
self._setup_config(canonical='', whitelist=set(['rock']))
|
||||
self.assertEqual(self.plugin._resolve_genres(['delta blues']),
|
||||
'')
|
||||
u'')
|
||||
|
||||
def test_count(self):
|
||||
"""Keep the n first genres, as we expect them to be sorted from more to
|
||||
|
|
@ -99,7 +98,7 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
count=2)
|
||||
self.assertEqual(self.plugin._resolve_genres(
|
||||
['jazz', 'pop', 'rock', 'blues']),
|
||||
'Jazz, Rock')
|
||||
u'Jazz, Rock')
|
||||
|
||||
def test_count_c14n(self):
|
||||
"""Keep the n first genres, after having applied c14n when necessary
|
||||
|
|
@ -111,14 +110,14 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
# second slot
|
||||
self.assertEqual(self.plugin._resolve_genres(
|
||||
['jazz', 'pop', 'country blues', 'rock']),
|
||||
'Jazz, Blues')
|
||||
u'Jazz, Blues')
|
||||
|
||||
def test_c14n_whitelist(self):
|
||||
"""Genres first pass through c14n and are then filtered
|
||||
"""
|
||||
self._setup_config(canonical=True, whitelist=set(['rock']))
|
||||
self.assertEqual(self.plugin._resolve_genres(['delta blues']),
|
||||
'')
|
||||
u'')
|
||||
|
||||
def test_empty_string_enables_canonical(self):
|
||||
"""For backwards compatibility, setting the `canonical` option
|
||||
|
|
@ -126,7 +125,7 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
"""
|
||||
self._setup_config(canonical='', count=99)
|
||||
self.assertEqual(self.plugin._resolve_genres(['delta blues']),
|
||||
'Blues')
|
||||
u'Blues')
|
||||
|
||||
def test_empty_string_enables_whitelist(self):
|
||||
"""Again for backwards compatibility, setting the `whitelist`
|
||||
|
|
@ -134,14 +133,14 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
"""
|
||||
self._setup_config(whitelist='')
|
||||
self.assertEqual(self.plugin._resolve_genres(['iota blues']),
|
||||
'')
|
||||
u'')
|
||||
|
||||
def test_no_duplicate(self):
|
||||
"""Remove duplicated genres.
|
||||
"""
|
||||
self._setup_config(count=99)
|
||||
self.assertEqual(self.plugin._resolve_genres(['blues', 'blues']),
|
||||
'Blues')
|
||||
u'Blues')
|
||||
|
||||
def test_tags_for(self):
|
||||
class MockPylastElem(object):
|
||||
|
|
@ -189,29 +188,29 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
|
|||
|
||||
config['lastgenre'] = {'force': False}
|
||||
res = self.plugin._get_genre(item)
|
||||
self.assertEqual(res, (item.genre, 'keep'))
|
||||
self.assertEqual(res, (item.genre, u'keep'))
|
||||
|
||||
config['lastgenre'] = {'force': True, 'source': 'track'}
|
||||
config['lastgenre'] = {'force': True, 'source': u'track'}
|
||||
res = self.plugin._get_genre(item)
|
||||
self.assertEqual(res, (MOCK_GENRES['track'], 'track'))
|
||||
self.assertEqual(res, (MOCK_GENRES['track'], u'track'))
|
||||
|
||||
config['lastgenre'] = {'source': 'album'}
|
||||
config['lastgenre'] = {'source': u'album'}
|
||||
res = self.plugin._get_genre(item)
|
||||
self.assertEqual(res, (MOCK_GENRES['album'], 'album'))
|
||||
self.assertEqual(res, (MOCK_GENRES['album'], u'album'))
|
||||
|
||||
config['lastgenre'] = {'source': 'artist'}
|
||||
config['lastgenre'] = {'source': u'artist'}
|
||||
res = self.plugin._get_genre(item)
|
||||
self.assertEqual(res, (MOCK_GENRES['artist'], 'artist'))
|
||||
self.assertEqual(res, (MOCK_GENRES['artist'], u'artist'))
|
||||
|
||||
MOCK_GENRES['artist'] = None
|
||||
res = self.plugin._get_genre(item)
|
||||
self.assertEqual(res, (item.genre, 'original'))
|
||||
self.assertEqual(res, (item.genre, u'original'))
|
||||
|
||||
config['lastgenre'] = {'fallback': 'rap'}
|
||||
config['lastgenre'] = {'fallback': u'rap'}
|
||||
item.genre = None
|
||||
res = self.plugin._get_genre(item)
|
||||
self.assertEqual(res, (config['lastgenre']['fallback'].get(),
|
||||
'fallback'))
|
||||
u'fallback'))
|
||||
|
||||
|
||||
def suite():
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
"""Tests for non-query database functions of Item.
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
|
@ -47,12 +46,12 @@ np = util.normpath
|
|||
class LoadTest(_common.LibTestCase):
|
||||
def test_load_restores_data_from_db(self):
|
||||
original_title = self.i.title
|
||||
self.i.title = 'something'
|
||||
self.i.title = u'something'
|
||||
self.i.load()
|
||||
self.assertEqual(original_title, self.i.title)
|
||||
|
||||
def test_load_clears_dirty_flags(self):
|
||||
self.i.artist = 'something'
|
||||
self.i.artist = u'something'
|
||||
self.assertTrue('artist' in self.i._dirty)
|
||||
self.i.load()
|
||||
self.assertTrue('artist' not in self.i._dirty)
|
||||
|
|
@ -69,7 +68,7 @@ class StoreTest(_common.LibTestCase):
|
|||
|
||||
def test_store_only_writes_dirty_fields(self):
|
||||
original_genre = self.i.genre
|
||||
self.i._values_fixed['genre'] = 'beatboxing' # change w/o dirtying
|
||||
self.i._values_fixed['genre'] = u'beatboxing' # change w/o dirtying
|
||||
self.i.store()
|
||||
new_genre = self.lib._connection().execute(
|
||||
'select genre from items where '
|
||||
|
|
@ -77,7 +76,7 @@ class StoreTest(_common.LibTestCase):
|
|||
self.assertEqual(new_genre, original_genre)
|
||||
|
||||
def test_store_clears_dirty_flags(self):
|
||||
self.i.composer = 'tvp'
|
||||
self.i.composer = u'tvp'
|
||||
self.i.store()
|
||||
self.assertTrue('composer' not in self.i._dirty)
|
||||
|
||||
|
|
@ -131,7 +130,7 @@ class GetSetTest(_common.TestCase):
|
|||
self.assertTrue('title' not in self.i._dirty)
|
||||
|
||||
def test_invalid_field_raises_attributeerror(self):
|
||||
self.assertRaises(AttributeError, getattr, self.i, 'xyzzy')
|
||||
self.assertRaises(AttributeError, getattr, self.i, u'xyzzy')
|
||||
|
||||
|
||||
class DestinationTest(_common.TestCase):
|
||||
|
|
@ -150,17 +149,17 @@ class DestinationTest(_common.TestCase):
|
|||
|
||||
def test_directory_works_with_trailing_slash(self):
|
||||
self.lib.directory = 'one/'
|
||||
self.lib.path_formats = [('default', 'two')]
|
||||
self.lib.path_formats = [(u'default', u'two')]
|
||||
self.assertEqual(self.i.destination(), np('one/two'))
|
||||
|
||||
def test_directory_works_without_trailing_slash(self):
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = [('default', 'two')]
|
||||
self.lib.path_formats = [(u'default', u'two')]
|
||||
self.assertEqual(self.i.destination(), np('one/two'))
|
||||
|
||||
def test_destination_substitues_metadata_values(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_formats = [('default', '$album/$artist $title')]
|
||||
self.lib.path_formats = [(u'default', u'$album/$artist $title')]
|
||||
self.i.title = 'three'
|
||||
self.i.artist = 'two'
|
||||
self.i.album = 'one'
|
||||
|
|
@ -169,22 +168,22 @@ class DestinationTest(_common.TestCase):
|
|||
|
||||
def test_destination_preserves_extension(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_formats = [('default', '$title')]
|
||||
self.lib.path_formats = [(u'default', u'$title')]
|
||||
self.i.path = 'hey.audioformat'
|
||||
self.assertEqual(self.i.destination(),
|
||||
np('base/the title.audioformat'))
|
||||
|
||||
def test_lower_case_extension(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_formats = [('default', '$title')]
|
||||
self.lib.path_formats = [(u'default', u'$title')]
|
||||
self.i.path = 'hey.MP3'
|
||||
self.assertEqual(self.i.destination(),
|
||||
np('base/the title.mp3'))
|
||||
|
||||
def test_destination_pads_some_indices(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_formats = [('default',
|
||||
'$track $tracktotal $disc $disctotal $bpm')]
|
||||
self.lib.path_formats = [(u'default',
|
||||
u'$track $tracktotal $disc $disctotal $bpm')]
|
||||
self.i.track = 1
|
||||
self.i.tracktotal = 2
|
||||
self.i.disc = 3
|
||||
|
|
@ -195,7 +194,7 @@ class DestinationTest(_common.TestCase):
|
|||
|
||||
def test_destination_pads_date_values(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_formats = [('default', '$year-$month-$day')]
|
||||
self.lib.path_formats = [(u'default', u'$year-$month-$day')]
|
||||
self.i.year = 1
|
||||
self.i.month = 2
|
||||
self.i.day = 3
|
||||
|
|
@ -222,13 +221,13 @@ class DestinationTest(_common.TestCase):
|
|||
self.assertTrue(os.path.join('one', 'two') in dest)
|
||||
|
||||
def test_destination_long_names_truncated(self):
|
||||
self.i.title = 'X' * 300
|
||||
self.i.artist = 'Y' * 300
|
||||
self.i.title = u'X' * 300
|
||||
self.i.artist = u'Y' * 300
|
||||
for c in self.i.destination().split(os.path.sep):
|
||||
self.assertTrue(len(c) <= 255)
|
||||
|
||||
def test_destination_long_names_keep_extension(self):
|
||||
self.i.title = 'X' * 300
|
||||
self.i.title = u'X' * 300
|
||||
self.i.path = 'something.extn'
|
||||
dest = self.i.destination()
|
||||
self.assertEqual(dest[-5:], '.extn')
|
||||
|
|
@ -243,7 +242,7 @@ class DestinationTest(_common.TestCase):
|
|||
self.assertFalse('two / three' in p)
|
||||
|
||||
def test_path_with_format(self):
|
||||
self.lib.path_formats = [('default', '$artist/$album ($format)')]
|
||||
self.lib.path_formats = [(u'default', u'$artist/$album ($format)')]
|
||||
p = self.i.destination()
|
||||
self.assert_('(FLAC)' in p)
|
||||
|
||||
|
|
@ -251,7 +250,7 @@ class DestinationTest(_common.TestCase):
|
|||
i1, i2 = item(), item()
|
||||
self.lib.add_album([i1, i2])
|
||||
i1.year, i2.year = 2009, 2010
|
||||
self.lib.path_formats = [('default', '$album ($year)/$track $title')]
|
||||
self.lib.path_formats = [(u'default', u'$album ($year)/$track $title')]
|
||||
dest1, dest2 = i1.destination(), i2.destination()
|
||||
self.assertEqual(os.path.dirname(dest1), os.path.dirname(dest2))
|
||||
|
||||
|
|
@ -259,17 +258,17 @@ class DestinationTest(_common.TestCase):
|
|||
self.i.comp = False
|
||||
self.lib.add_album([self.i])
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = [('default', 'two'),
|
||||
('comp:true', 'three')]
|
||||
self.lib.path_formats = [(u'default', u'two'),
|
||||
(u'comp:true', u'three')]
|
||||
self.assertEqual(self.i.destination(), np('one/two'))
|
||||
|
||||
def test_singleton_path(self):
|
||||
i = item(self.lib)
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = [
|
||||
('default', 'two'),
|
||||
('singleton:true', 'four'),
|
||||
('comp:true', 'three'),
|
||||
(u'default', u'two'),
|
||||
(u'singleton:true', u'four'),
|
||||
(u'comp:true', u'three'),
|
||||
]
|
||||
self.assertEqual(i.destination(), np('one/four'))
|
||||
|
||||
|
|
@ -278,9 +277,9 @@ class DestinationTest(_common.TestCase):
|
|||
i.comp = True
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = [
|
||||
('default', 'two'),
|
||||
('comp:true', 'three'),
|
||||
('singleton:true', 'four'),
|
||||
(u'default', u'two'),
|
||||
(u'comp:true', u'three'),
|
||||
(u'singleton:true', u'four'),
|
||||
]
|
||||
self.assertEqual(i.destination(), np('one/three'))
|
||||
|
||||
|
|
@ -289,32 +288,32 @@ class DestinationTest(_common.TestCase):
|
|||
self.lib.add_album([self.i])
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = [
|
||||
('default', 'two'),
|
||||
('comp:true', 'three'),
|
||||
(u'default', u'two'),
|
||||
(u'comp:true', u'three'),
|
||||
]
|
||||
self.assertEqual(self.i.destination(), np('one/three'))
|
||||
|
||||
def test_albumtype_query_path(self):
|
||||
self.i.comp = True
|
||||
self.lib.add_album([self.i])
|
||||
self.i.albumtype = 'sometype'
|
||||
self.i.albumtype = u'sometype'
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = [
|
||||
('default', 'two'),
|
||||
('albumtype:sometype', 'four'),
|
||||
('comp:true', 'three'),
|
||||
(u'default', u'two'),
|
||||
(u'albumtype:sometype', u'four'),
|
||||
(u'comp:true', u'three'),
|
||||
]
|
||||
self.assertEqual(self.i.destination(), np('one/four'))
|
||||
|
||||
def test_albumtype_path_fallback_to_comp(self):
|
||||
self.i.comp = True
|
||||
self.lib.add_album([self.i])
|
||||
self.i.albumtype = 'sometype'
|
||||
self.i.albumtype = u'sometype'
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = [
|
||||
('default', 'two'),
|
||||
('albumtype:anothertype', 'four'),
|
||||
('comp:true', 'three'),
|
||||
(u'default', u'two'),
|
||||
(u'albumtype:anothertype', u'four'),
|
||||
(u'comp:true', u'three'),
|
||||
]
|
||||
self.assertEqual(self.i.destination(), np('one/three'))
|
||||
|
||||
|
|
@ -356,42 +355,42 @@ class DestinationTest(_common.TestCase):
|
|||
self.assertEqual(val, u'')
|
||||
|
||||
def test_artist_falls_back_to_albumartist(self):
|
||||
self.i.artist = ''
|
||||
self.i.albumartist = 'something'
|
||||
self.lib.path_formats = [('default', '$artist')]
|
||||
self.i.artist = u''
|
||||
self.i.albumartist = u'something'
|
||||
self.lib.path_formats = [(u'default', u'$artist')]
|
||||
p = self.i.destination()
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], 'something')
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], u'something')
|
||||
|
||||
def test_albumartist_falls_back_to_artist(self):
|
||||
self.i.artist = 'trackartist'
|
||||
self.i.albumartist = ''
|
||||
self.lib.path_formats = [('default', '$albumartist')]
|
||||
self.i.artist = u'trackartist'
|
||||
self.i.albumartist = u''
|
||||
self.lib.path_formats = [(u'default', u'$albumartist')]
|
||||
p = self.i.destination()
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], 'trackartist')
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], u'trackartist')
|
||||
|
||||
def test_artist_overrides_albumartist(self):
|
||||
self.i.artist = 'theartist'
|
||||
self.i.albumartist = 'something'
|
||||
self.lib.path_formats = [('default', '$artist')]
|
||||
self.i.artist = u'theartist'
|
||||
self.i.albumartist = u'something'
|
||||
self.lib.path_formats = [(u'default', u'$artist')]
|
||||
p = self.i.destination()
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], 'theartist')
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], u'theartist')
|
||||
|
||||
def test_albumartist_overrides_artist(self):
|
||||
self.i.artist = 'theartist'
|
||||
self.i.albumartist = 'something'
|
||||
self.lib.path_formats = [('default', '$albumartist')]
|
||||
self.i.artist = u'theartist'
|
||||
self.i.albumartist = u'something'
|
||||
self.lib.path_formats = [(u'default', u'$albumartist')]
|
||||
p = self.i.destination()
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], 'something')
|
||||
self.assertEqual(p.rsplit(os.path.sep, 1)[1], u'something')
|
||||
|
||||
def test_unicode_normalized_nfd_on_mac(self):
|
||||
instr = unicodedata.normalize('NFC', u'caf\xe9')
|
||||
self.lib.path_formats = [('default', instr)]
|
||||
self.lib.path_formats = [(u'default', instr)]
|
||||
dest = self.i.destination(platform='darwin', fragment=True)
|
||||
self.assertEqual(dest, unicodedata.normalize('NFD', instr))
|
||||
|
||||
def test_unicode_normalized_nfc_on_linux(self):
|
||||
instr = unicodedata.normalize('NFD', u'caf\xe9')
|
||||
self.lib.path_formats = [('default', instr)]
|
||||
self.lib.path_formats = [(u'default', instr)]
|
||||
dest = self.i.destination(platform='linux2', fragment=True)
|
||||
self.assertEqual(dest, unicodedata.normalize('NFC', instr))
|
||||
|
||||
|
|
@ -400,7 +399,7 @@ class DestinationTest(_common.TestCase):
|
|||
sys.getfilesystemencoding = lambda: 'mbcs'
|
||||
try:
|
||||
self.i.title = u'h\u0259d'
|
||||
self.lib.path_formats = [('default', '$title')]
|
||||
self.lib.path_formats = [(u'default', u'$title')]
|
||||
p = self.i.destination()
|
||||
self.assertFalse(b'?' in p)
|
||||
# We use UTF-8 to encode Windows paths now.
|
||||
|
|
@ -409,7 +408,7 @@ class DestinationTest(_common.TestCase):
|
|||
sys.getfilesystemencoding = oldfunc
|
||||
|
||||
def test_unicode_extension_in_fragment(self):
|
||||
self.lib.path_formats = [('default', u'foo')]
|
||||
self.lib.path_formats = [(u'default', u'foo')]
|
||||
self.i.path = util.bytestring_path(u'bar.caf\xe9')
|
||||
dest = self.i.destination(platform='linux2', fragment=True)
|
||||
self.assertEqual(dest, u'foo.caf\xe9')
|
||||
|
|
@ -418,16 +417,16 @@ class DestinationTest(_common.TestCase):
|
|||
config['asciify_paths'] = True
|
||||
self.lib.replacements = [(re.compile(u'"'), u'q')]
|
||||
self.lib.directory = 'lib'
|
||||
self.lib.path_formats = [('default', '$title')]
|
||||
self.lib.path_formats = [(u'default', u'$title')]
|
||||
self.i.title = u'\u201c\u00f6\u2014\u00cf\u201d'
|
||||
self.assertEqual(self.i.destination(), np('lib/qo--Iq'))
|
||||
|
||||
def test_destination_with_replacements(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.replacements = [(re.compile(r'a'), u'e')]
|
||||
self.lib.path_formats = [('default', '$album/$title')]
|
||||
self.i.title = 'foo'
|
||||
self.i.album = 'bar'
|
||||
self.lib.path_formats = [(u'default', u'$album/$title')]
|
||||
self.i.title = u'foo'
|
||||
self.i.album = u'bar'
|
||||
self.assertEqual(self.i.destination(),
|
||||
np('base/ber/foo'))
|
||||
|
||||
|
|
@ -435,11 +434,11 @@ class DestinationTest(_common.TestCase):
|
|||
def test_destination_with_empty_component(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.replacements = [(re.compile(r'^$'), u'_')]
|
||||
self.lib.path_formats = [('default', '$album/$artist/$title')]
|
||||
self.i.title = 'three'
|
||||
self.i.artist = ''
|
||||
self.i.albumartist = ''
|
||||
self.i.album = 'one'
|
||||
self.lib.path_formats = [(u'default', u'$album/$artist/$title')]
|
||||
self.i.title = u'three'
|
||||
self.i.artist = u''
|
||||
self.i.albumartist = u''
|
||||
self.i.album = u'one'
|
||||
self.assertEqual(self.i.destination(),
|
||||
np('base/one/_/three'))
|
||||
|
||||
|
|
@ -447,9 +446,9 @@ class DestinationTest(_common.TestCase):
|
|||
def test_destination_with_empty_final_component(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.replacements = [(re.compile(r'^$'), u'_')]
|
||||
self.lib.path_formats = [('default', '$album/$title')]
|
||||
self.i.title = ''
|
||||
self.i.album = 'one'
|
||||
self.lib.path_formats = [(u'default', u'$album/$title')]
|
||||
self.i.title = u''
|
||||
self.i.album = u'one'
|
||||
self.i.path = 'foo.mp3'
|
||||
self.assertEqual(self.i.destination(),
|
||||
np('base/one/_.mp3'))
|
||||
|
|
@ -463,11 +462,11 @@ class DestinationTest(_common.TestCase):
|
|||
|
||||
# Construct an item whose untruncated path ends with a Y but whose
|
||||
# truncated version ends with an X.
|
||||
self.i.title = 'X' * 300 + 'Y'
|
||||
self.i.title = u'X' * 300 + u'Y'
|
||||
|
||||
# The final path should reflect the replacement.
|
||||
dest = self.i.destination()
|
||||
self.assertEqual(dest[-2:], 'XZ')
|
||||
self.assertEqual(dest[-2:], u'XZ')
|
||||
|
||||
def test_legalize_path_one_for_many_replacement(self):
|
||||
# Use a replacement that should always replace the last X in any
|
||||
|
|
@ -478,18 +477,18 @@ class DestinationTest(_common.TestCase):
|
|||
|
||||
# Construct an item whose untruncated path ends with a Y but whose
|
||||
# truncated version ends with an X.
|
||||
self.i.title = 'X' * 300 + 'Y'
|
||||
self.i.title = u'X' * 300 + u'Y'
|
||||
|
||||
# The final path should ignore the user replacement and create a path
|
||||
# of the correct length, containing Xs.
|
||||
dest = self.i.destination()
|
||||
self.assertEqual(dest[-2:], 'XX')
|
||||
self.assertEqual(dest[-2:], u'XX')
|
||||
|
||||
|
||||
class ItemFormattedMappingTest(_common.LibTestCase):
|
||||
def test_formatted_item_value(self):
|
||||
formatted = self.i.formatted()
|
||||
self.assertEqual(formatted['artist'], 'the artist')
|
||||
self.assertEqual(formatted['artist'], u'the artist')
|
||||
|
||||
def test_get_unset_field(self):
|
||||
formatted = self.i.formatted()
|
||||
|
|
@ -502,53 +501,53 @@ class ItemFormattedMappingTest(_common.LibTestCase):
|
|||
|
||||
def test_get_method_with_specified_default(self):
|
||||
formatted = self.i.formatted()
|
||||
self.assertEqual(formatted.get('other_field', 'default'), 'default')
|
||||
self.assertEqual(formatted.get('other_field', u'default'), u'default')
|
||||
|
||||
def test_item_precedence(self):
|
||||
album = self.lib.add_album([self.i])
|
||||
album['artist'] = 'foo'
|
||||
album['artist'] = u'foo'
|
||||
album.store()
|
||||
self.assertNotEqual('foo', self.i.formatted().get('artist'))
|
||||
self.assertNotEqual(u'foo', self.i.formatted().get('artist'))
|
||||
|
||||
def test_album_flex_field(self):
|
||||
album = self.lib.add_album([self.i])
|
||||
album['flex'] = 'foo'
|
||||
album['flex'] = u'foo'
|
||||
album.store()
|
||||
self.assertEqual('foo', self.i.formatted().get('flex'))
|
||||
self.assertEqual(u'foo', self.i.formatted().get('flex'))
|
||||
|
||||
def test_album_field_overrides_item_field_for_path(self):
|
||||
# Make the album inconsistent with the item.
|
||||
album = self.lib.add_album([self.i])
|
||||
album.album = 'foo'
|
||||
album.album = u'foo'
|
||||
album.store()
|
||||
self.i.album = 'bar'
|
||||
self.i.album = u'bar'
|
||||
self.i.store()
|
||||
|
||||
# Ensure the album takes precedence.
|
||||
formatted = self.i.formatted(for_path=True)
|
||||
self.assertEqual(formatted['album'], 'foo')
|
||||
self.assertEqual(formatted['album'], u'foo')
|
||||
|
||||
def test_artist_falls_back_to_albumartist(self):
|
||||
self.i.artist = ''
|
||||
self.i.artist = u''
|
||||
formatted = self.i.formatted()
|
||||
self.assertEqual(formatted['artist'], 'the album artist')
|
||||
self.assertEqual(formatted['artist'], u'the album artist')
|
||||
|
||||
def test_albumartist_falls_back_to_artist(self):
|
||||
self.i.albumartist = ''
|
||||
self.i.albumartist = u''
|
||||
formatted = self.i.formatted()
|
||||
self.assertEqual(formatted['albumartist'], 'the artist')
|
||||
self.assertEqual(formatted['albumartist'], u'the artist')
|
||||
|
||||
def test_both_artist_and_albumartist_empty(self):
|
||||
self.i.artist = ''
|
||||
self.i.albumartist = ''
|
||||
self.i.artist = u''
|
||||
self.i.albumartist = u''
|
||||
formatted = self.i.formatted()
|
||||
self.assertEqual(formatted['albumartist'], '')
|
||||
self.assertEqual(formatted['albumartist'], u'')
|
||||
|
||||
|
||||
class PathFormattingMixin(object):
|
||||
"""Utilities for testing path formatting."""
|
||||
def _setf(self, fmt):
|
||||
self.lib.path_formats.insert(0, ('default', fmt))
|
||||
self.lib.path_formats.insert(0, (u'default', fmt))
|
||||
|
||||
def _assert_dest(self, dest, i=None):
|
||||
if i is None:
|
||||
|
|
@ -563,7 +562,7 @@ class DestinationFunctionTest(_common.TestCase, PathFormattingMixin):
|
|||
super(DestinationFunctionTest, self).setUp()
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.lib.directory = '/base'
|
||||
self.lib.path_formats = [('default', u'path')]
|
||||
self.lib.path_formats = [(u'default', u'path')]
|
||||
self.i = item(self.lib)
|
||||
|
||||
def tearDown(self):
|
||||
|
|
@ -624,7 +623,7 @@ class DisambiguationTest(_common.TestCase, PathFormattingMixin):
|
|||
super(DisambiguationTest, self).setUp()
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.lib.directory = '/base'
|
||||
self.lib.path_formats = [('default', u'path')]
|
||||
self.lib.path_formats = [(u'default', u'path')]
|
||||
|
||||
self.i1 = item()
|
||||
self.i1.year = 2001
|
||||
|
|
@ -645,14 +644,14 @@ class DisambiguationTest(_common.TestCase, PathFormattingMixin):
|
|||
|
||||
def test_unique_with_default_arguments_uses_albumtype(self):
|
||||
album2 = self.lib.get_album(self.i1)
|
||||
album2.albumtype = 'bar'
|
||||
album2.albumtype = u'bar'
|
||||
album2.store()
|
||||
self._setf(u'foo%aunique{}/$title')
|
||||
self._assert_dest('/base/foo [bar]/the title', self.i1)
|
||||
|
||||
def test_unique_expands_to_nothing_for_distinct_albums(self):
|
||||
album2 = self.lib.get_album(self.i2)
|
||||
album2.album = 'different album'
|
||||
album2.album = u'different album'
|
||||
album2.store()
|
||||
|
||||
self._assert_dest('/base/foo/the title', self.i1)
|
||||
|
|
@ -673,7 +672,7 @@ class DisambiguationTest(_common.TestCase, PathFormattingMixin):
|
|||
album2 = self.lib.get_album(self.i2)
|
||||
album2.year = 2001
|
||||
album1 = self.lib.get_album(self.i1)
|
||||
album1.albumtype = 'foo/bar'
|
||||
album1.albumtype = u'foo/bar'
|
||||
album2.store()
|
||||
album1.store()
|
||||
self._setf(u'foo%aunique{albumartist album,albumtype}/$title')
|
||||
|
|
@ -698,7 +697,7 @@ class PluginDestinationTest(_common.TestCase):
|
|||
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.lib.directory = '/base'
|
||||
self.lib.path_formats = [('default', u'$artist $foo')]
|
||||
self.lib.path_formats = [(u'default', u'$artist $foo')]
|
||||
self.i = item(self.lib)
|
||||
|
||||
def tearDown(self):
|
||||
|
|
@ -711,25 +710,25 @@ class PluginDestinationTest(_common.TestCase):
|
|||
self.assertEqual(the_dest, '/base/' + dest)
|
||||
|
||||
def test_undefined_value_not_substituted(self):
|
||||
self._assert_dest('the artist $foo')
|
||||
self._assert_dest(u'the artist $foo')
|
||||
|
||||
def test_plugin_value_not_substituted(self):
|
||||
self._tv_map = {
|
||||
'foo': 'bar',
|
||||
}
|
||||
self._assert_dest('the artist bar')
|
||||
self._assert_dest(u'the artist bar')
|
||||
|
||||
def test_plugin_value_overrides_attribute(self):
|
||||
self._tv_map = {
|
||||
'artist': 'bar',
|
||||
}
|
||||
self._assert_dest('bar $foo')
|
||||
self._assert_dest(u'bar $foo')
|
||||
|
||||
def test_plugin_value_sanitized(self):
|
||||
self._tv_map = {
|
||||
'foo': 'bar/baz',
|
||||
}
|
||||
self._assert_dest('the artist bar_baz')
|
||||
self._assert_dest(u'the artist bar_baz')
|
||||
|
||||
|
||||
class AlbumInfoTest(_common.TestCase):
|
||||
|
|
@ -766,7 +765,7 @@ class AlbumInfoTest(_common.TestCase):
|
|||
|
||||
def test_individual_tracks_have_no_albuminfo(self):
|
||||
i2 = item()
|
||||
i2.album = 'aTotallyDifferentAlbum'
|
||||
i2.album = u'aTotallyDifferentAlbum'
|
||||
self.lib.add(i2)
|
||||
ai = self.lib.get_album(i2)
|
||||
self.assertEqual(ai, None)
|
||||
|
|
@ -782,29 +781,29 @@ class AlbumInfoTest(_common.TestCase):
|
|||
if i.id == self.i.id:
|
||||
break
|
||||
else:
|
||||
self.fail("item not found")
|
||||
self.fail(u"item not found")
|
||||
|
||||
def test_albuminfo_changes_affect_items(self):
|
||||
ai = self.lib.get_album(self.i)
|
||||
ai.album = 'myNewAlbum'
|
||||
ai.album = u'myNewAlbum'
|
||||
ai.store()
|
||||
i = self.lib.items()[0]
|
||||
self.assertEqual(i.album, 'myNewAlbum')
|
||||
self.assertEqual(i.album, u'myNewAlbum')
|
||||
|
||||
def test_albuminfo_change_albumartist_changes_items(self):
|
||||
ai = self.lib.get_album(self.i)
|
||||
ai.albumartist = 'myNewArtist'
|
||||
ai.albumartist = u'myNewArtist'
|
||||
ai.store()
|
||||
i = self.lib.items()[0]
|
||||
self.assertEqual(i.albumartist, 'myNewArtist')
|
||||
self.assertNotEqual(i.artist, 'myNewArtist')
|
||||
self.assertEqual(i.albumartist, u'myNewArtist')
|
||||
self.assertNotEqual(i.artist, u'myNewArtist')
|
||||
|
||||
def test_albuminfo_change_artist_does_not_change_items(self):
|
||||
ai = self.lib.get_album(self.i)
|
||||
ai.artist = 'myNewArtist'
|
||||
ai.artist = u'myNewArtist'
|
||||
ai.store()
|
||||
i = self.lib.items()[0]
|
||||
self.assertNotEqual(i.artist, 'myNewArtist')
|
||||
self.assertNotEqual(i.artist, u'myNewArtist')
|
||||
|
||||
def test_albuminfo_remove_removes_items(self):
|
||||
item_id = self.i.id
|
||||
|
|
@ -821,7 +820,7 @@ class AlbumInfoTest(_common.TestCase):
|
|||
|
||||
def test_noop_albuminfo_changes_affect_items(self):
|
||||
i = self.lib.items()[0]
|
||||
i.album = 'foobar'
|
||||
i.album = u'foobar'
|
||||
i.store()
|
||||
ai = self.lib.get_album(self.i)
|
||||
ai.album = ai.album
|
||||
|
|
@ -882,7 +881,7 @@ class PathStringTest(_common.TestCase):
|
|||
self.assert_(isinstance(i.path, bytes))
|
||||
|
||||
def test_special_chars_preserved_in_database(self):
|
||||
path = 'b\xe1r'.encode('utf8')
|
||||
path = u'b\xe1r'.encode('utf8')
|
||||
self.i.path = path
|
||||
self.i.store()
|
||||
i = list(self.lib.items())[0]
|
||||
|
|
@ -890,7 +889,7 @@ class PathStringTest(_common.TestCase):
|
|||
|
||||
def test_special_char_path_added_to_database(self):
|
||||
self.i.remove()
|
||||
path = 'b\xe1r'.encode('utf8')
|
||||
path = u'b\xe1r'.encode('utf8')
|
||||
i = item()
|
||||
i.path = path
|
||||
self.lib.add(i)
|
||||
|
|
@ -962,16 +961,16 @@ class MtimeTest(_common.TestCase):
|
|||
self.assertGreaterEqual(self.i.mtime, self._mtime())
|
||||
|
||||
def test_mtime_reset_on_db_modify(self):
|
||||
self.i.title = 'something else'
|
||||
self.i.title = u'something else'
|
||||
self.assertLess(self.i.mtime, self._mtime())
|
||||
|
||||
def test_mtime_up_to_date_after_write(self):
|
||||
self.i.title = 'something else'
|
||||
self.i.title = u'something else'
|
||||
self.i.write()
|
||||
self.assertGreaterEqual(self.i.mtime, self._mtime())
|
||||
|
||||
def test_mtime_up_to_date_after_read(self):
|
||||
self.i.title = 'something else'
|
||||
self.i.title = u'something else'
|
||||
self.i.read()
|
||||
self.assertGreaterEqual(self.i.mtime, self._mtime())
|
||||
|
||||
|
|
@ -996,19 +995,19 @@ class TemplateTest(_common.LibTestCase):
|
|||
def test_year_formatted_in_template(self):
|
||||
self.i.year = 123
|
||||
self.i.store()
|
||||
self.assertEqual(self.i.evaluate_template('$year'), '0123')
|
||||
self.assertEqual(self.i.evaluate_template('$year'), u'0123')
|
||||
|
||||
def test_album_flexattr_appears_in_item_template(self):
|
||||
self.album = self.lib.add_album([self.i])
|
||||
self.album.foo = 'baz'
|
||||
self.album.foo = u'baz'
|
||||
self.album.store()
|
||||
self.assertEqual(self.i.evaluate_template('$foo'), 'baz')
|
||||
self.assertEqual(self.i.evaluate_template('$foo'), u'baz')
|
||||
|
||||
def test_album_and_item_format(self):
|
||||
config['format_album'] = u'foö $foo'
|
||||
album = beets.library.Album()
|
||||
album.foo = 'bar'
|
||||
album.tagada = 'togodo'
|
||||
album.foo = u'bar'
|
||||
album.tagada = u'togodo'
|
||||
self.assertEqual(u"{0}".format(album), u"foö bar")
|
||||
self.assertEqual(u"{0:$tagada}".format(album), u"togodo")
|
||||
self.assertEqual(unicode(album), u"foö bar")
|
||||
|
|
@ -1016,10 +1015,10 @@ class TemplateTest(_common.LibTestCase):
|
|||
|
||||
config['format_item'] = 'bar $foo'
|
||||
item = beets.library.Item()
|
||||
item.foo = 'bar'
|
||||
item.tagada = 'togodo'
|
||||
self.assertEqual("{0}".format(item), "bar bar")
|
||||
self.assertEqual("{0:$tagada}".format(item), "togodo")
|
||||
item.foo = u'bar'
|
||||
item.tagada = u'togodo'
|
||||
self.assertEqual("{0}".format(item), u"bar bar")
|
||||
self.assertEqual("{0:$tagada}".format(item), u"togodo")
|
||||
|
||||
|
||||
class UnicodePathTest(_common.LibTestCase):
|
||||
|
|
@ -1079,7 +1078,7 @@ class WriteTest(unittest.TestCase, TestHelper):
|
|||
# Since `date` is not a MediaField, this should do nothing.
|
||||
item = self.add_item_fixture()
|
||||
clean_year = item.year
|
||||
item.date = 'foo'
|
||||
item.date = u'foo'
|
||||
item.write()
|
||||
self.assertEqual(MediaFile(item.path).year, clean_year)
|
||||
|
||||
|
|
@ -1119,7 +1118,7 @@ class FilesizeTest(unittest.TestCase, TestHelper):
|
|||
class ParseQueryTest(unittest.TestCase):
|
||||
def test_parse_invalid_query_string(self):
|
||||
with self.assertRaises(beets.dbcore.InvalidQueryError) as raised:
|
||||
beets.library.parse_query_string('foo"', None)
|
||||
beets.library.parse_query_string(u'foo"', None)
|
||||
self.assertIsInstance(raised.exception,
|
||||
beets.dbcore.query.ParsingError)
|
||||
|
||||
|
|
@ -1139,9 +1138,9 @@ class LibraryFieldTypesTest(unittest.TestCase):
|
|||
self.assertEqual(time_local, t.format(123456789))
|
||||
# parse
|
||||
self.assertEqual(123456789.0, t.parse(time_local))
|
||||
self.assertEqual(123456789.0, t.parse('123456789.0'))
|
||||
self.assertEqual(t.null, t.parse('not123456789.0'))
|
||||
self.assertEqual(t.null, t.parse('1973-11-29'))
|
||||
self.assertEqual(123456789.0, t.parse(u'123456789.0'))
|
||||
self.assertEqual(t.null, t.parse(u'not123456789.0'))
|
||||
self.assertEqual(t.null, t.parse(u'1973-11-29'))
|
||||
|
||||
def test_pathtype(self):
|
||||
t = beets.library.PathType()
|
||||
|
|
@ -1157,23 +1156,23 @@ class LibraryFieldTypesTest(unittest.TestCase):
|
|||
t = beets.library.MusicalKey()
|
||||
|
||||
# parse
|
||||
self.assertEqual('C#m', t.parse('c#m'))
|
||||
self.assertEqual('Gm', t.parse('g minor'))
|
||||
self.assertEqual('Not c#m', t.parse('not C#m'))
|
||||
self.assertEqual(u'C#m', t.parse(u'c#m'))
|
||||
self.assertEqual(u'Gm', t.parse(u'g minor'))
|
||||
self.assertEqual(u'Not c#m', t.parse(u'not C#m'))
|
||||
|
||||
def test_durationtype(self):
|
||||
t = beets.library.DurationType()
|
||||
|
||||
# format
|
||||
self.assertEqual('1:01', t.format(61.23))
|
||||
self.assertEqual('60:01', t.format(3601.23))
|
||||
self.assertEqual('0:00', t.format(None))
|
||||
self.assertEqual(u'1:01', t.format(61.23))
|
||||
self.assertEqual(u'60:01', t.format(3601.23))
|
||||
self.assertEqual(u'0:00', t.format(None))
|
||||
# parse
|
||||
self.assertEqual(61.0, t.parse('1:01'))
|
||||
self.assertEqual(61.23, t.parse('61.23'))
|
||||
self.assertEqual(3601.0, t.parse('60:01'))
|
||||
self.assertEqual(t.null, t.parse('1:00:01'))
|
||||
self.assertEqual(t.null, t.parse('not61.23'))
|
||||
self.assertEqual(61.0, t.parse(u'1:01'))
|
||||
self.assertEqual(61.23, t.parse(u'61.23'))
|
||||
self.assertEqual(3601.0, t.parse(u'60:01'))
|
||||
self.assertEqual(t.null, t.parse(u'1:00:01'))
|
||||
self.assertEqual(t.null, t.parse(u'not61.23'))
|
||||
# config format_raw_length
|
||||
beets.config['format_raw_length'] = True
|
||||
self.assertEqual(61.23, t.format(61.23))
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue