mirror of
https://github.com/beetbox/beets.git
synced 2025-12-23 17:13:30 +01:00
path_sep_replace config option
I also took this opportunity to move and rename util.santize_for_path to library.format_for_path, which was long overdue.
This commit is contained in:
parent
09d6eedd6a
commit
b9cb3980c2
5 changed files with 47 additions and 38 deletions
|
|
@ -26,6 +26,7 @@ replace:
|
|||
'[<>:"\?\*\|]': _
|
||||
'\.$': _
|
||||
'\s+$': ''
|
||||
path_sep_replace: _
|
||||
art_filename: cover
|
||||
|
||||
plugins: []
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ from beets import util
|
|||
from beets.util import bytestring_path, syspath, normpath, samefile,\
|
||||
displayable_path
|
||||
from beets.util.functemplate import Template
|
||||
import beets
|
||||
|
||||
MAX_FILENAME_LENGTH = 200
|
||||
|
||||
|
|
@ -183,6 +184,39 @@ def _regexp(expr, val):
|
|||
return False
|
||||
return res is not None
|
||||
|
||||
# Path element formatting for templating.
|
||||
def format_for_path(value, key=None, pathmod=None):
|
||||
"""Sanitize the value for inclusion in a path: replace separators
|
||||
with _, etc. Doesn't guarantee that the whole path will be valid;
|
||||
you should still call `util.sanitize_path` on the complete path.
|
||||
"""
|
||||
pathmod = pathmod or os.path
|
||||
|
||||
if isinstance(value, basestring):
|
||||
for sep in (pathmod.sep, pathmod.altsep):
|
||||
if sep:
|
||||
value = value.replace(
|
||||
sep,
|
||||
beets.config['path_sep_replace'].get(unicode),
|
||||
)
|
||||
elif key in ('track', 'tracktotal', 'disc', 'disctotal'):
|
||||
# Pad indices with zeros.
|
||||
value = u'%02i' % (value or 0)
|
||||
elif key == 'year':
|
||||
value = u'%04i' % (value or 0)
|
||||
elif key in ('month', 'day'):
|
||||
value = u'%02i' % (value or 0)
|
||||
elif key == 'bitrate':
|
||||
# Bitrate gets formatted as kbps.
|
||||
value = u'%ikbps' % ((value or 0) // 1000)
|
||||
elif key == 'samplerate':
|
||||
# Sample rate formatted as kHz.
|
||||
value = u'%ikHz' % ((value or 0) // 1000)
|
||||
else:
|
||||
value = unicode(value)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
# Exceptions.
|
||||
|
||||
|
|
@ -361,7 +395,7 @@ class Item(object):
|
|||
# From Item.
|
||||
value = getattr(self, key)
|
||||
if sanitize:
|
||||
value = util.sanitize_for_path(value, pathmod, key)
|
||||
value = format_for_path(value, key, pathmod)
|
||||
mapping[key] = value
|
||||
|
||||
# Additional fields in non-sanitized case.
|
||||
|
|
@ -378,7 +412,7 @@ class Item(object):
|
|||
# Get values from plugins.
|
||||
for key, value in plugins.template_values(self).iteritems():
|
||||
if sanitize:
|
||||
value = util.sanitize_for_path(value, pathmod, key)
|
||||
value = format_for_path(value, key, pathmod)
|
||||
mapping[key] = value
|
||||
|
||||
# Get template functions.
|
||||
|
|
@ -1568,7 +1602,7 @@ class Album(BaseAlbum):
|
|||
if not isinstance(self._library.art_filename,Template):
|
||||
self._library.art_filename = Template(self._library.art_filename)
|
||||
|
||||
subpath = util.sanitize_path(util.sanitize_for_path(
|
||||
subpath = util.sanitize_path(format_for_path(
|
||||
self.evaluate_template(self._library.art_filename)
|
||||
))
|
||||
subpath = bytestring_path(subpath)
|
||||
|
|
@ -1764,8 +1798,8 @@ class DefaultTemplateFunctions(object):
|
|||
return res
|
||||
|
||||
# Flatten disambiguation value into a string.
|
||||
disam_value = util.sanitize_for_path(getattr(album, disambiguator),
|
||||
self.pathmod, disambiguator)
|
||||
disam_value = format_for_path(getattr(album, disambiguator),
|
||||
disambiguator, self.pathmod)
|
||||
res = u' [{0}]'.format(disam_value)
|
||||
self.lib._memotable[memokey] = res
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -494,35 +494,6 @@ def truncate_path(path, pathmod=None, length=MAX_FILENAME_LENGTH):
|
|||
|
||||
return pathmod.join(*out)
|
||||
|
||||
def sanitize_for_path(value, pathmod=None, key=None):
|
||||
"""Sanitize the value for inclusion in a path: replace separators
|
||||
with _, etc. Doesn't guarantee that the whole path will be valid;
|
||||
you should still call sanitize_path on the complete path.
|
||||
"""
|
||||
pathmod = pathmod or os.path
|
||||
|
||||
if isinstance(value, basestring):
|
||||
for sep in (pathmod.sep, pathmod.altsep):
|
||||
if sep:
|
||||
value = value.replace(sep, u'_')
|
||||
elif key in ('track', 'tracktotal', 'disc', 'disctotal'):
|
||||
# Pad indices with zeros.
|
||||
value = u'%02i' % (value or 0)
|
||||
elif key == 'year':
|
||||
value = u'%04i' % (value or 0)
|
||||
elif key in ('month', 'day'):
|
||||
value = u'%02i' % (value or 0)
|
||||
elif key == 'bitrate':
|
||||
# Bitrate gets formatted as kbps.
|
||||
value = u'%ikbps' % ((value or 0) // 1000)
|
||||
elif key == 'samplerate':
|
||||
# Sample rate formatted as kHz.
|
||||
value = u'%ikHz' % ((value or 0) // 1000)
|
||||
else:
|
||||
value = unicode(value)
|
||||
|
||||
return value
|
||||
|
||||
def str2bool(value):
|
||||
"""Returns a boolean reflecting a human-entered string."""
|
||||
if value.lower() in ('yes', '1', 'true', 't', 'y'):
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ New configuration options:
|
|||
* :doc:`/plugins/lastgenre`: A new configuration option lets you choose to
|
||||
retrieve artist-level tags as genres instead of album- or track-level tags.
|
||||
Thanks to Peter Fern and Peter Schnebel.
|
||||
* You can now customize the character substituted for path separators (e.g., /)
|
||||
in filenames via ``path_sep_replace``. The default is an underscore. Use this
|
||||
setting with caution.
|
||||
|
||||
Other new stuff:
|
||||
|
||||
|
|
|
|||
|
|
@ -347,19 +347,19 @@ class DestinationTest(unittest.TestCase):
|
|||
|
||||
def test_component_sanitize_replaces_separators(self):
|
||||
name = posixpath.join('a', 'b')
|
||||
newname = util.sanitize_for_path(name, posixpath)
|
||||
newname = beets.library.format_for_path(name, None, posixpath)
|
||||
self.assertNotEqual(name, newname)
|
||||
|
||||
def test_component_sanitize_pads_with_zero(self):
|
||||
name = util.sanitize_for_path(1, posixpath, 'track')
|
||||
name = beets.library.format_for_path(1, 'track', posixpath)
|
||||
self.assertTrue(name.startswith('0'))
|
||||
|
||||
def test_component_sanitize_uses_kbps_bitrate(self):
|
||||
val = util.sanitize_for_path(12345, posixpath, 'bitrate')
|
||||
val = beets.library.format_for_path(12345, 'bitrate', posixpath)
|
||||
self.assertEqual(val, u'12kbps')
|
||||
|
||||
def test_component_sanitize_uses_khz_samplerate(self):
|
||||
val = util.sanitize_for_path(12345, posixpath, 'samplerate')
|
||||
val = beets.library.format_for_path(12345, 'samplerate', posixpath)
|
||||
self.assertEqual(val, u'12kHz')
|
||||
|
||||
def test_artist_falls_back_to_albumartist(self):
|
||||
|
|
|
|||
Loading…
Reference in a new issue