Skip to content

Naming sanitization leaves double spaces, trailing non-breaking spaces, and glues elided words (apostrophes) #12244

Description

@Mika3578

Describe the bug

The naming/sanitization helpers used when building file and folder names produce malformed names in several cases. All of them come from sanitize_filename() (medusa/helper/common.py#L295) and sanitize_scene_name() (medusa/helpers/init.py#L748), which is used by the %S.N / %E.N tokens via dot() in medusa/tv/episode.py.

  1. Double spaces after removing forbidden characters. sanitize_filename() deletes :"<>|? without collapsing the surrounding whitespace. A show named Example Show : Subtitle becomes Example Show Subtitle (2016) (double space). A name ending in ? can leave a double space before an appended year.

  2. Non-breaking space (U+00A0) is never handled. It is not converted to a separator by sanitize_scene_name(), not removed by sanitize_filename(), and not stripped by the final strip(' .'). Episode titles from TheTVDB can contain NBSP. Result: filenames that end with an invisible non-breaking space, which breaks later path comparisons. Tabs were handled in Strip more invalid filename characters - Fixes #3839 #4351; NBSP remains.

  3. Deleted apostrophes glue words together. sanitize_scene_name() deletes ' and ' outright. Fine for English scene style (Bob's Burgers -> Bobs.Burgers), but for elisions it merges two different words: L'exemple -> Lexemple, C'est un titre -> Cest.un.titre.

  4. Minor, same code path: an empty episode title yields a double dot (Show.S01E01..(2025-01-15)), and %E_N/%S_N leave trailing underscores when a title ends in ?/! because us() converts spaces before punctuation is removed.

To Reproduce

from medusa.helper.common import sanitize_filename
from medusa.helpers import sanitize_scene_name

sanitize_filename('Example Show : Subtitle')   # 'Example Show  Subtitle' (double space)
sanitize_filename('example title\xa0?')          # 'example title\xa0' (trailing NBSP)
sanitize_scene_name("L'exemple de titre")      # 'Lexemple.de.titre' (glued words)

Or add a show whose name contains : and let Medusa create the show folder.

Expected behavior

  • Removing a forbidden character should not leave double separators; e.g. Sonarr collapses repeated separators with ([- ._])(\1)+ and uses a "smart" colon replacement (": " -> " - ", remaining : -> -) in FileNameBuilder.CleanFileName.
  • NBSP should be treated like a regular space (or stripped at the ends like a space).
  • Apostrophe removal in the naming context should insert a separator instead of merging words (search/scene matching in name_cache.py / scene_exceptions.py must keep the current behavior, so the change belongs in the dot() naming helper, not in the shared sanitize_scene_name()).

Medusa (please complete the following information):

  • OS: Windows 10 (also reproducible on any OS; code path is platform-independent)
  • Branch: develop
  • Commit: b935f77 (current develop; both functions unchanged)
  • Python version: 3.13
  • Database version: n/a (not DB related)

Additional context

Related history: #3839 / #4351 added \t and control characters to sanitize_filename(); NBSP was the remaining invisible character.

Edited to remove personal library show/folder names from the original report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions