diff --git a/beets/util/m3u.py b/beets/util/m3u.py index b6e355e06..60202b404 100644 --- a/beets/util/m3u.py +++ b/beets/util/m3u.py @@ -79,19 +79,22 @@ class M3UFile: Handles the creation of potential parent directories. """ + # Use bytes for header if extm3u is True, otherwise use str header = [b"#EXTM3U"] if self.extm3u else [] + if not self.media_list: raise EmptyPlaylistError - contents = header + self.media_list + + # Ensure all media_list items are bytes + media_bytes = [ + line.encode('utf-8') if isinstance(line, str) else line + for line in self.media_list + ] + + contents = header + media_bytes pl_normpath = normpath(self.path) mkdirall(pl_normpath) - try: - with open(syspath(pl_normpath), "wb") as pl_file: - for line in contents: - pl_file.write(line + b"\n") - pl_file.write(b"\n") # Final linefeed to prevent noeol file. - except OSError as exc: - raise FilesystemError( - exc, "create", (pl_normpath,), traceback.format_exc() - ) + with open(syspath(pl_normpath), "wb") as pl_file: + for line in contents: + pl_file.write(line + b"\n") diff --git a/beetsplug/convert.py b/beetsplug/convert.py index e72f8c75a..b22b60d53 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -616,24 +616,29 @@ class ConvertPlugin(BeetsPlugin): ) if playlist: - # Playlist paths are understood as relative to the dest directory. pl_normpath = util.normpath(playlist) pl_dir = os.path.dirname(pl_normpath) self._log.info("Creating playlist file {}", pl_normpath) - # Generates a list of paths to media files, ensures the paths are - # relative to the playlist's location and translates the unicode - # strings we get from item.destination to bytes. - items_paths = [ - os.path.relpath( - item.destination(basedir=dest, path_formats=path_formats), - pl_dir, + + items_paths = [] + for item in items: + path = ( + item.path + if item.path + else item.destination(basedir=dest, path_formats=path_formats) ) - for item in items - ] + rel_path = os.path.relpath(path, pl_dir) + + # Ensure string encoding for playlist entries (convert bytes to str) + if isinstance(rel_path, bytes): + rel_path = rel_path.decode('utf-8', errors='replace') + + items_paths.append(rel_path) + if not pretend: m3ufile = M3UFile(playlist) m3ufile.set_contents(items_paths) - m3ufile.write() + m3ufile.write() # Assume m3ufile.write expects str lines def convert_on_import(self, lib, item): """Transcode a file automatically after it is imported into the