mirror of
https://github.com/beetbox/beets.git
synced 2025-12-28 03:22:39 +01:00
More flake fixes
This commit is contained in:
parent
4a2f0d11b8
commit
bc55747abc
6 changed files with 142 additions and 108 deletions
3
beet
3
beet
|
|
@ -10,7 +10,7 @@
|
|||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
|
|
@ -18,4 +18,3 @@ import beets.ui
|
|||
|
||||
if __name__ == '__main__':
|
||||
beets.ui.main()
|
||||
|
||||
|
|
|
|||
|
|
@ -104,8 +104,11 @@ class StringFieldQuery(FieldQuery):
|
|||
class SubstringQuery(StringFieldQuery):
|
||||
"""A query that matches a substring in a specific item field."""
|
||||
def col_clause(self):
|
||||
search = '%' + (self.pattern.replace('\\','\\\\').replace('%','\\%')
|
||||
.replace('_','\\_')) + '%'
|
||||
pattern = (self.pattern
|
||||
.replace('\\', '\\\\')
|
||||
.replace('%', '\\%')
|
||||
.replace('_', '\\_'))
|
||||
search = '%' + pattern + '%'
|
||||
clause = self.field + " like ? escape '\\'"
|
||||
subvals = [search]
|
||||
return clause, subvals
|
||||
|
|
@ -236,12 +239,16 @@ class CollectionQuery(Query):
|
|||
self.subqueries = subqueries
|
||||
|
||||
# Act like a sequence.
|
||||
|
||||
def __len__(self):
|
||||
return len(self.subqueries)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.subqueries[key]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.subqueries)
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.subqueries
|
||||
|
||||
|
|
@ -334,10 +341,8 @@ class FalseQuery(Query):
|
|||
return False
|
||||
|
||||
|
||||
|
||||
# Time/date queries.
|
||||
|
||||
|
||||
def _to_epoch_time(date):
|
||||
"""Convert a `datetime` object to an integer number of seconds since
|
||||
the (local) Unix epoch.
|
||||
|
|
|
|||
|
|
@ -18,10 +18,8 @@ from . import query
|
|||
from beets.util import str2bool
|
||||
|
||||
|
||||
|
||||
# Abstract base.
|
||||
|
||||
|
||||
class Type(object):
|
||||
"""An object encapsulating the type of a model field. Includes
|
||||
information about how to store, query, format, and parse a given
|
||||
|
|
@ -63,10 +61,8 @@ class Type(object):
|
|||
return value
|
||||
|
||||
|
||||
|
||||
# Reusable types.
|
||||
|
||||
|
||||
class Integer(Type):
|
||||
"""A basic integer type.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -53,34 +53,8 @@ import enum
|
|||
|
||||
__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile']
|
||||
|
||||
|
||||
|
||||
# Logger.
|
||||
log = logging.getLogger('beets')
|
||||
|
||||
|
||||
|
||||
# Exceptions.
|
||||
|
||||
class UnreadableFileError(Exception):
|
||||
"""Indicates a file that MediaFile can't read.
|
||||
"""
|
||||
pass
|
||||
|
||||
class FileTypeError(UnreadableFileError):
|
||||
"""Raised for files that don't seem to have a type MediaFile
|
||||
supports.
|
||||
"""
|
||||
pass
|
||||
|
||||
class MutagenError(UnreadableFileError):
|
||||
"""Raised when Mutagen fails unexpectedly---probably due to a bug.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# Constants.
|
||||
|
||||
# Human-readable type names.
|
||||
TYPES = {
|
||||
'mp3': 'MP3',
|
||||
|
|
@ -96,6 +70,25 @@ TYPES = {
|
|||
}
|
||||
|
||||
|
||||
# Exceptions.
|
||||
|
||||
class UnreadableFileError(Exception):
|
||||
"""Indicates a file that MediaFile can't read.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class FileTypeError(UnreadableFileError):
|
||||
"""Raised for files that don't seem to have a type MediaFile
|
||||
supports.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class MutagenError(UnreadableFileError):
|
||||
"""Raised when Mutagen fails unexpectedly---probably due to a bug.
|
||||
"""
|
||||
|
||||
|
||||
# Utility.
|
||||
|
||||
|
|
@ -161,7 +154,6 @@ def _safe_cast(out_type, val):
|
|||
return val
|
||||
|
||||
|
||||
|
||||
# Image coding for ASF/WMA.
|
||||
|
||||
def _unpack_asf_image(data):
|
||||
|
|
@ -189,6 +181,7 @@ def _unpack_asf_image(data):
|
|||
return (mime.decode("utf-16-le"), image_data, type,
|
||||
description.decode("utf-16-le"))
|
||||
|
||||
|
||||
def _pack_asf_image(mime, data, type=3, description=""):
|
||||
"""Pack image data for a WM/Picture tag.
|
||||
"""
|
||||
|
|
@ -199,7 +192,6 @@ def _pack_asf_image(mime, data, type=3, description=""):
|
|||
return tag_data
|
||||
|
||||
|
||||
|
||||
# iTunes Sound Check encoding.
|
||||
|
||||
def _sc_decode(soundcheck):
|
||||
|
|
@ -237,6 +229,7 @@ def _sc_decode(soundcheck):
|
|||
|
||||
return round(gain, 2), round(peak, 6)
|
||||
|
||||
|
||||
def _sc_encode(gain, peak):
|
||||
"""Encode ReplayGain gain/peak values as a Sound Check string.
|
||||
"""
|
||||
|
|
@ -261,10 +254,8 @@ def _sc_encode(gain, peak):
|
|||
return (u' %08X' * 10) % values
|
||||
|
||||
|
||||
|
||||
# Cover art and other images.
|
||||
|
||||
|
||||
def _image_mime_type(data):
|
||||
"""Return the MIME type of the image data (a bytestring).
|
||||
"""
|
||||
|
|
@ -341,7 +332,6 @@ class Image(object):
|
|||
return self.type.value
|
||||
|
||||
|
||||
|
||||
# StorageStyle classes describe strategies for accessing values in
|
||||
# Mutagen file objects.
|
||||
|
||||
|
|
@ -417,7 +407,7 @@ class StorageStyle(object):
|
|||
return the represented value.
|
||||
"""
|
||||
if self.suffix and isinstance(mutagen_value, unicode) \
|
||||
and mutagen_value.endswith(self.suffix):
|
||||
and mutagen_value.endswith(self.suffix):
|
||||
return mutagen_value[:-len(self.suffix)]
|
||||
else:
|
||||
return mutagen_value
|
||||
|
|
@ -588,10 +578,11 @@ class MP4ListStorageStyle(ListStorageStyle, MP4StorageStyle):
|
|||
|
||||
|
||||
class MP4SoundCheckStorageStyle(SoundCheckStorageStyleMixin, MP4StorageStyle):
|
||||
def __init__(self, index=0, **kwargs):
|
||||
super(MP4SoundCheckStorageStyle, self).__init__(**kwargs)
|
||||
def __init__(self, key, index=0, **kwargs):
|
||||
super(MP4SoundCheckStorageStyle, self).__init__(key, **kwargs)
|
||||
self.index = index
|
||||
|
||||
|
||||
class MP4BoolStorageStyle(MP4StorageStyle):
|
||||
"""A style for booleans in MPEG-4 files. (MPEG-4 has an atom type
|
||||
specifically for representing booleans.)
|
||||
|
|
@ -716,7 +707,10 @@ class MP3DescStorageStyle(MP3StorageStyle):
|
|||
# need to make a new frame?
|
||||
if not found:
|
||||
frame = mutagen.id3.Frames[self.key](
|
||||
desc=str(self.description), text=value, encoding=3)
|
||||
desc=str(self.description),
|
||||
text=value,
|
||||
encoding=3
|
||||
)
|
||||
if self.id3_lang:
|
||||
frame.lang = self.id3_lang
|
||||
mutagen_file.tags.add(frame)
|
||||
|
|
@ -832,7 +826,8 @@ class VorbisImageStorageStyle(ListStorageStyle):
|
|||
|
||||
def __init__(self):
|
||||
super(VorbisImageStorageStyle, self).__init__(
|
||||
key='metadata_block_picture')
|
||||
key='metadata_block_picture'
|
||||
)
|
||||
self.as_type = str
|
||||
|
||||
def fetch(self, mutagen_file):
|
||||
|
|
@ -849,7 +844,7 @@ class VorbisImageStorageStyle(ListStorageStyle):
|
|||
except (TypeError, AttributeError):
|
||||
continue
|
||||
images.append(Image(data=pic.data, desc=pic.desc,
|
||||
type=pic.type))
|
||||
type=pic.type))
|
||||
return images
|
||||
|
||||
def store(self, mutagen_file, image_data):
|
||||
|
|
@ -904,7 +899,6 @@ class FlacImageStorageStyle(ListStorageStyle):
|
|||
return pic
|
||||
|
||||
|
||||
|
||||
# MediaField is a descriptor that represents a single logical field. It
|
||||
# aggregates several StorageStyles describing how to access the data for
|
||||
# each file type.
|
||||
|
|
@ -1141,10 +1135,8 @@ class ImageListField(MediaField):
|
|||
style.set_list(mediafile.mgfile, images)
|
||||
|
||||
|
||||
|
||||
# MediaFile is a collection of fields.
|
||||
|
||||
|
||||
class MediaFile(object):
|
||||
"""Represents a multimedia file on disk and provides access to its
|
||||
metadata.
|
||||
|
|
@ -1186,10 +1178,11 @@ class MediaFile(object):
|
|||
log.error('uncaught Mutagen exception in open: {0}'.format(exc))
|
||||
raise MutagenError('Mutagen raised an exception')
|
||||
|
||||
if self.mgfile is None: # Mutagen couldn't guess the type
|
||||
if self.mgfile is None:
|
||||
# Mutagen couldn't guess the type
|
||||
raise FileTypeError('file type unsupported by Mutagen')
|
||||
elif type(self.mgfile).__name__ == 'M4A' or \
|
||||
type(self.mgfile).__name__ == 'MP4':
|
||||
elif (type(self.mgfile).__name__ == 'M4A' or
|
||||
type(self.mgfile).__name__ == 'MP4'):
|
||||
# This hack differentiates AAC and ALAC until we find a more
|
||||
# deterministic approach. Mutagen only sets the sample rate
|
||||
# for AAC files. See:
|
||||
|
|
@ -1199,8 +1192,8 @@ class MediaFile(object):
|
|||
self.type = 'aac'
|
||||
else:
|
||||
self.type = 'alac'
|
||||
elif type(self.mgfile).__name__ == 'ID3' or \
|
||||
type(self.mgfile).__name__ == 'MP3':
|
||||
elif (type(self.mgfile).__name__ == 'ID3' or
|
||||
type(self.mgfile).__name__ == 'MP3'):
|
||||
self.type = 'mp3'
|
||||
elif type(self.mgfile).__name__ == 'FLAC':
|
||||
self.type = 'flac'
|
||||
|
|
@ -1261,7 +1254,6 @@ class MediaFile(object):
|
|||
for tag in self.mgfile.keys():
|
||||
del self.mgfile[tag]
|
||||
|
||||
|
||||
# Convenient access to the set of available fields.
|
||||
|
||||
@classmethod
|
||||
|
|
@ -1313,7 +1305,6 @@ class MediaFile(object):
|
|||
if field in dict:
|
||||
setattr(self, field, dict[field])
|
||||
|
||||
|
||||
# Field definitions.
|
||||
|
||||
title = MediaField(
|
||||
|
|
@ -1487,8 +1478,8 @@ class MediaFile(object):
|
|||
)
|
||||
country = MediaField(
|
||||
MP3DescStorageStyle('MusicBrainz Album Release Country'),
|
||||
MP4StorageStyle("----:com.apple.iTunes:MusicBrainz Album "
|
||||
"Release Country"),
|
||||
MP4StorageStyle("----:com.apple.iTunes:MusicBrainz "
|
||||
"Album Release Country"),
|
||||
StorageStyle('RELEASECOUNTRY'),
|
||||
ASFStorageStyle('MusicBrainz/Album Release Country'),
|
||||
)
|
||||
|
|
@ -1603,63 +1594,101 @@ class MediaFile(object):
|
|||
|
||||
# ReplayGain fields.
|
||||
rg_track_gain = MediaField(
|
||||
MP3DescStorageStyle(u'REPLAYGAIN_TRACK_GAIN',
|
||||
float_places=2, suffix=u' dB'),
|
||||
MP3DescStorageStyle(u'replaygain_track_gain',
|
||||
float_places=2, suffix=u' dB'),
|
||||
MP3SoundCheckStorageStyle(key='COMM', index=0, desc=u'iTunNORM',
|
||||
id3_lang='eng'),
|
||||
MP4StorageStyle(key='----:com.apple.iTunes:replaygain_track_gain',
|
||||
float_places=2, suffix=b' dB'),
|
||||
MP4SoundCheckStorageStyle(key='----:com.apple.iTunes:iTunNORM',
|
||||
index=0),
|
||||
StorageStyle(u'REPLAYGAIN_TRACK_GAIN',
|
||||
float_places=2, suffix=u' dB'),
|
||||
ASFStorageStyle(u'replaygain_track_gain',
|
||||
float_places=2, suffix=u' dB'),
|
||||
MP3DescStorageStyle(
|
||||
u'REPLAYGAIN_TRACK_GAIN',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
MP3DescStorageStyle(
|
||||
u'replaygain_track_gain',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
MP3SoundCheckStorageStyle(
|
||||
key='COMM',
|
||||
index=0, desc=u'iTunNORM',
|
||||
id3_lang='eng'
|
||||
),
|
||||
MP4StorageStyle(
|
||||
'----:com.apple.iTunes:replaygain_track_gain',
|
||||
float_places=2, suffix=b' dB'
|
||||
),
|
||||
MP4SoundCheckStorageStyle(
|
||||
'----:com.apple.iTunes:iTunNORM',
|
||||
index=0
|
||||
),
|
||||
StorageStyle(
|
||||
u'REPLAYGAIN_TRACK_GAIN',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
ASFStorageStyle(
|
||||
u'replaygain_track_gain',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
out_type=float
|
||||
)
|
||||
rg_album_gain = MediaField(
|
||||
MP3DescStorageStyle(u'REPLAYGAIN_ALBUM_GAIN',
|
||||
float_places=2, suffix=u' dB'),
|
||||
MP3DescStorageStyle(u'replaygain_album_gain',
|
||||
float_places=2, suffix=u' dB'),
|
||||
MP4SoundCheckStorageStyle(key='----:com.apple.iTunes:iTunNORM',
|
||||
index=1),
|
||||
StorageStyle(u'REPLAYGAIN_ALBUM_GAIN',
|
||||
float_places=2, suffix=u' dB'),
|
||||
ASFStorageStyle(u'replaygain_album_gain',
|
||||
float_places=2, suffix=u' dB'),
|
||||
MP3DescStorageStyle(
|
||||
u'REPLAYGAIN_ALBUM_GAIN',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
MP3DescStorageStyle(
|
||||
u'replaygain_album_gain',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
MP4SoundCheckStorageStyle(
|
||||
'----:com.apple.iTunes:iTunNORM',
|
||||
index=1
|
||||
),
|
||||
StorageStyle(
|
||||
u'REPLAYGAIN_ALBUM_GAIN',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
ASFStorageStyle(
|
||||
u'replaygain_album_gain',
|
||||
float_places=2, suffix=u' dB'
|
||||
),
|
||||
out_type=float
|
||||
)
|
||||
rg_track_peak = MediaField(
|
||||
MP3DescStorageStyle(u'REPLAYGAIN_TRACK_PEAK',
|
||||
float_places=6),
|
||||
MP3DescStorageStyle(u'replaygain_track_peak',
|
||||
float_places=6),
|
||||
MP3SoundCheckStorageStyle(key='COMM', index=1, desc=u'iTunNORM',
|
||||
id3_lang='eng'),
|
||||
MP4StorageStyle('----:com.apple.iTunes:replaygain_track_peak',
|
||||
float_places=6),
|
||||
MP4SoundCheckStorageStyle(key='----:com.apple.iTunes:iTunNORM',
|
||||
index=1),
|
||||
StorageStyle(u'REPLAYGAIN_TRACK_PEAK',
|
||||
float_places=6),
|
||||
ASFStorageStyle(u'replaygain_track_peak',
|
||||
float_places=6),
|
||||
MP3DescStorageStyle(
|
||||
u'REPLAYGAIN_TRACK_PEAK',
|
||||
float_places=6
|
||||
),
|
||||
MP3DescStorageStyle(
|
||||
u'replaygain_track_peak',
|
||||
float_places=6
|
||||
),
|
||||
MP3SoundCheckStorageStyle(
|
||||
key=u'COMM',
|
||||
index=1, desc=u'iTunNORM',
|
||||
id3_lang='eng'
|
||||
),
|
||||
MP4StorageStyle(
|
||||
'----:com.apple.iTunes:replaygain_track_peak',
|
||||
float_places=6
|
||||
),
|
||||
MP4SoundCheckStorageStyle(
|
||||
'----:com.apple.iTunes:iTunNORM',
|
||||
index=1
|
||||
),
|
||||
StorageStyle(u'REPLAYGAIN_TRACK_PEAK', float_places=6),
|
||||
ASFStorageStyle(u'replaygain_track_peak', float_places=6),
|
||||
out_type=float,
|
||||
)
|
||||
rg_album_peak = MediaField(
|
||||
MP3DescStorageStyle(u'REPLAYGAIN_ALBUM_PEAK',
|
||||
float_places=6),
|
||||
MP3DescStorageStyle(u'replaygain_album_peak',
|
||||
float_places=6),
|
||||
MP4StorageStyle('----:com.apple.iTunes:replaygain_album_peak',
|
||||
float_places=6),
|
||||
StorageStyle(u'REPLAYGAIN_ALBUM_PEAK',
|
||||
float_places=6),
|
||||
ASFStorageStyle(u'replaygain_album_peak',
|
||||
float_places=6),
|
||||
MP3DescStorageStyle(
|
||||
u'REPLAYGAIN_ALBUM_PEAK',
|
||||
float_places=6
|
||||
),
|
||||
MP3DescStorageStyle(
|
||||
u'replaygain_album_peak',
|
||||
float_places=6
|
||||
),
|
||||
MP4StorageStyle(
|
||||
'----:com.apple.iTunes:replaygain_album_peak',
|
||||
float_places=6
|
||||
),
|
||||
StorageStyle(u'REPLAYGAIN_ALBUM_PEAK', float_places=6),
|
||||
ASFStorageStyle(u'replaygain_album_peak', float_places=6),
|
||||
out_type=float,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ def ancestry(path):
|
|||
break
|
||||
last_path = path
|
||||
|
||||
if path: # don't yield ''
|
||||
if path:
|
||||
# don't yield ''
|
||||
out.insert(0, path)
|
||||
return out
|
||||
|
||||
|
|
@ -477,6 +478,8 @@ CHAR_REPLACE = [
|
|||
(re.compile(ur'\.$'), u'_'), # Trailing dots.
|
||||
(re.compile(ur'\s+$'), u''), # Trailing whitespace.
|
||||
]
|
||||
|
||||
|
||||
def sanitize_path(path, replacements=None):
|
||||
"""Takes a path (as a Unicode string) and makes sure that it is
|
||||
legal. Returns a new path. Only works with fragments; won't work
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from beets import util
|
|||
|
||||
Node = namedtuple('Node', ['files', 'dirs'])
|
||||
|
||||
|
||||
def _insert(node, path, itemid):
|
||||
"""Insert an item into a virtual filesystem node."""
|
||||
if len(path) == 1:
|
||||
|
|
@ -33,6 +34,7 @@ def _insert(node, path, itemid):
|
|||
node.dirs[dirname] = Node({}, {})
|
||||
_insert(node.dirs[dirname], rest, itemid)
|
||||
|
||||
|
||||
def libtree(lib):
|
||||
"""Generates a filesystem-like directory tree for the files
|
||||
contained in `lib`. Filesystem nodes are (files, dirs) named
|
||||
|
|
|
|||
Loading…
Reference in a new issue