diff --git a/beets/library.py b/beets/library.py index caf132ce4..12e16e07c 100644 --- a/beets/library.py +++ b/beets/library.py @@ -1152,6 +1152,7 @@ class Library(BaseLibrary): # Encode for the filesystem. if not fragment: subpath = bytestring_path(subpath) + subpath = util.truncate_path(subpath, pathmod) # Preserve extension. _, extension = pathmod.splitext(item.path) diff --git a/beets/util/__init__.py b/beets/util/__init__.py index caba758e1..2b76a5dd0 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -452,16 +452,19 @@ def sanitize_path(path, pathmod=None, replacements=None): if not comps: return '' for i, comp in enumerate(comps): - # Replace special characters. for regex, repl in replacements: comp = regex.sub(repl, comp) - - # Truncate each component. - comp = comp[:MAX_FILENAME_LENGTH] - comps[i] = comp return pathmod.join(*comps) +def truncate_path(path, pathmod=None): + """Given a bytestring path or a Unicode path fragment, truncate the + components to a legal length. + """ + pathmod = pathmod or os.path + comps = [c[:MAX_FILENAME_LENGTH] for c in components(path, pathmod)] + return pathmod.join(*comps) + def sanitize_for_path(value, pathmod, key=None): """Sanitize the value for inclusion in a path: replace separators with _, etc. Doesn't guarantee that the whole path will be valid; diff --git a/docs/changelog.rst b/docs/changelog.rst index 910b9806c..76c3daf37 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -58,6 +58,8 @@ Changelog * Add the track mapping dictionary to the ``album_distance`` plugin function. * When an exception is raised while reading a file, the path of the file in question is now logged (thanks to Mike Kazantsev). +* Truncate long filenames based on their *bytes* rather than their Unicode + *characters*, fixing situations where encoded names could be too long. * Fix an assertion failure when the MusicBrainz main database and search server disagree. * Fix a bug that caused the :doc:`/plugins/lastgenre` and other plugins not to