Better handling of special chars in SMB filesystem
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 30 Nov 2025 10:09:19 +0000 (11:09 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 30 Nov 2025 10:09:19 +0000 (11:09 +0100)
music_assistant/providers/filesystem_local/__init__.py
music_assistant/providers/filesystem_local/helpers.py

index cb786c29ebde426bddb20e021c4f9be2b5a32395..269276e90d627e616bf57967456300dcc4a9aab7 100644 (file)
@@ -362,7 +362,16 @@ class LocalFileSystemProvider(MusicProvider):
                     if ext not in SUPPORTED_EXTENSIONS:
                         # skip unsupported file extension
                         continue
-                    yield FileSystemItem.from_dir_entry(item, self.base_path)
+                    try:
+                        yield FileSystemItem.from_dir_entry(item, self.base_path)
+                    except OSError as err:
+                        # Skip files that cannot be stat'd (e.g., invalid encoding on SMB mounts)
+                        # This typically happens with emoji or special unicode characters
+                        self.logger.debug(
+                            "Skipping file %s due to stat error: %s",
+                            item.path,
+                            str(err),
+                        )
 
         def run_sync() -> None:
             """Run the actual sync (in an executor job)."""
index 5b431adb4343548c72f3bfb173d2428643736b45..5c627ebb25e9adf7fbda258a281145c4eadb2686 100644 (file)
@@ -62,7 +62,10 @@ class FileSystemItem:
 
     @classmethod
     def from_dir_entry(cls, entry: os.DirEntry[str], base_path: str) -> FileSystemItem:
-        """Create FileSystemItem from os.DirEntry. NOT Async friendly."""
+        """Create FileSystemItem from os.DirEntry. NOT Async friendly.
+
+        :raises OSError: If the file cannot be stat'd (e.g., invalid filename encoding).
+        """
         if entry.is_dir(follow_symlinks=False):
             return cls(
                 filename=entry.name,
@@ -72,6 +75,8 @@ class FileSystemItem:
                 checksum=None,
                 file_size=None,
             )
+        # This can raise OSError for files with invalid encoding (e.g., emojis on SMB mounts)
+        # Let the caller handle the exception
         stat = entry.stat(follow_symlinks=False)
         return cls(
             filename=entry.name,
@@ -219,14 +224,20 @@ def sorted_scandir(base_path: str, sub_path: str, sort: bool = False) -> list[Fi
 
     if base_path not in sub_path:
         sub_path = os.path.join(base_path, sub_path)
-    items = [
-        FileSystemItem.from_dir_entry(x, base_path)
-        for x in os.scandir(sub_path)
+    items = []
+    for entry in os.scandir(sub_path):
         # filter out invalid dirs and hidden files
-        if (x.is_dir(follow_symlinks=False) or x.is_file(follow_symlinks=False))
-        and x.name not in IGNORE_DIRS
-        and not x.name.startswith(".")
-    ]
+        if not (entry.is_dir(follow_symlinks=False) or entry.is_file(follow_symlinks=False)):
+            continue
+        if entry.name in IGNORE_DIRS or entry.name.startswith("."):
+            continue
+        try:
+            items.append(FileSystemItem.from_dir_entry(entry, base_path))
+        except OSError:
+            # Skip files that cannot be stat'd (e.g., invalid encoding on SMB mounts)
+            # This typically happens with emoji or special unicode characters
+            continue
+
     if sort:
         return sorted(
             items,