mirror of
https://github.com/beetbox/beets.git
synced 2025-12-16 05:34:47 +01:00
Added loop to iterate over sanitize/truncate until stable. Enabled test_truncation_does_not_conflict_with_replacement test. Fixes #496.
This commit is contained in:
parent
18bd4471e4
commit
d07c8fde69
2 changed files with 66 additions and 21 deletions
|
|
@ -742,6 +742,71 @@ class Item(LibModel):
|
|||
|
||||
# Templating.
|
||||
|
||||
def _legalize_partial(self, path, fragment, replacements):
|
||||
""" Perform a partial legalization of the path (ie. a single
|
||||
sanitization and truncation). Outputs unicode if fragment is set,
|
||||
otherwise bytes.
|
||||
"""
|
||||
|
||||
# Truncate components and remove forbidden characters.
|
||||
path = util.sanitize_path(path, replacements)
|
||||
|
||||
# Encode for the filesystem.
|
||||
if not fragment:
|
||||
path = bytestring_path(path)
|
||||
|
||||
# Preserve extension.
|
||||
_, extension = os.path.splitext(self.path)
|
||||
if fragment:
|
||||
# Outputting Unicode.
|
||||
extension = extension.decode('utf8', 'ignore')
|
||||
path += extension.lower()
|
||||
|
||||
# Truncate too-long components.
|
||||
maxlen = beets.config['max_filename_length'].get(int)
|
||||
if not maxlen:
|
||||
# When zero, try to determine from filesystem.
|
||||
maxlen = util.max_filename_length(self._db.directory)
|
||||
path = util.truncate_path(path, maxlen)
|
||||
|
||||
return path
|
||||
|
||||
def _legalize_path(self, path, fragment):
|
||||
""" Perform several iterations of _legalize_partial, to generate a
|
||||
stable, optimal output path. This is necessary for cases where
|
||||
truncation produces unclean paths (eg. trailing space).
|
||||
"""
|
||||
|
||||
# Create a list of path candidates
|
||||
path_candidates = [b'']
|
||||
|
||||
replacements = self._db.replacements
|
||||
|
||||
# Perform an initial pass
|
||||
path = self._legalize_partial(path, fragment, replacements)
|
||||
while path != path_candidates[-1]:
|
||||
# This will keep sanitizing the path until it's stable, or an
|
||||
# infinite loop appears
|
||||
while path not in path_candidates:
|
||||
path_candidates.append(path)
|
||||
# Convert back to Unicode with extension removed
|
||||
print(util.displayable_path(path))
|
||||
path = os.path.splitext(util.displayable_path(path))[0]
|
||||
|
||||
# Run next pass
|
||||
path = self._legalize_partial(path, fragment, replacements)
|
||||
|
||||
# If an infinite loop occurred, adjust replacements to avoid it
|
||||
if path != path_candidates[-1]:
|
||||
replacements = dict((k, u'_') for k, v in replacements)
|
||||
|
||||
# If there's a rule to match a single underscore, set the
|
||||
# target to a blank string.
|
||||
if '_' in replacements:
|
||||
replacements['_'] = ''
|
||||
|
||||
return path
|
||||
|
||||
def destination(self, fragment=False, basedir=None, platform=None,
|
||||
path_formats=None):
|
||||
"""Returns the path in the library directory designated for the
|
||||
|
|
@ -790,26 +855,7 @@ class Item(LibModel):
|
|||
if beets.config['asciify_paths']:
|
||||
subpath = unidecode(subpath)
|
||||
|
||||
# Truncate components and remove forbidden characters.
|
||||
subpath = util.sanitize_path(subpath, self._db.replacements)
|
||||
|
||||
# Encode for the filesystem.
|
||||
if not fragment:
|
||||
subpath = bytestring_path(subpath)
|
||||
|
||||
# Preserve extension.
|
||||
_, extension = os.path.splitext(self.path)
|
||||
if fragment:
|
||||
# Outputting Unicode.
|
||||
extension = extension.decode('utf8', 'ignore')
|
||||
subpath += extension.lower()
|
||||
|
||||
# Truncate too-long components.
|
||||
maxlen = beets.config['max_filename_length'].get(int)
|
||||
if not maxlen:
|
||||
# When zero, try to determine from filesystem.
|
||||
maxlen = util.max_filename_length(self._db.directory)
|
||||
subpath = util.truncate_path(subpath, maxlen)
|
||||
subpath = self._legalize_path(subpath, fragment)
|
||||
|
||||
if fragment:
|
||||
return subpath
|
||||
|
|
|
|||
|
|
@ -452,7 +452,6 @@ class DestinationTest(_common.TestCase):
|
|||
self.assertEqual(self.i.destination(),
|
||||
np('base/one/_.mp3'))
|
||||
|
||||
@unittest.skip('unimplemented: #496')
|
||||
def test_truncation_does_not_conflict_with_replacement(self):
|
||||
# Use a replacement that should always replace the last X in any
|
||||
# path component with a Z.
|
||||
|
|
|
|||
Loading…
Reference in a new issue