fix(downloads): strip emoji from device filename + free disk when a download is deleted

Two UAT findings:
1. The device download filename (Content-Disposition) kept emoji/symbols from the video title.
   Add storage.display_filename (drops emoji/symbol/control unicode, keeps spaces + accents)
   and use it for the download name — readable and clean ("…alapján!.mp4", no emoji).
2. Deleting/canceling a download removed the job but the shared MediaAsset (and its file) lingered
   as cache, so 'Ready files' stayed inflated and disk wasn't freed. Rework: _release_asset drops
   the hold and, once no job holds the asset, deletes the file + row immediately (the cache only
   needs to span overlapping holders). Also fixes cancel never decrementing (it flipped status to
   'canceled' before releasing, tripping the holding-state guard).

Verified: filename emoji-stripped; enqueue→delete removes the asset row + file from disk.
This commit is contained in:
npeter83 2026-07-03 02:16:36 +02:00
parent 0045d41a74
commit f61ca96b6c
2 changed files with 31 additions and 11 deletions

View file

@ -63,6 +63,17 @@ def sanitize(name: str, limit: int = _MAX_TITLE) -> str:
return text or "untitled"
def display_filename(name: str) -> str:
"""User-facing download filename (Content-Disposition): strip emoji/symbols/control +
filesystem-illegal chars, but KEEP spaces, accents, and ordinary punctuation unlike the
underscore-joined on-disk name, this is the readable name the user saves to their device."""
text = unicodedata.normalize("NFKC", name or "")
text = "".join(ch for ch in text if unicodedata.category(ch) not in _DROP_CATEGORIES)
text = _ILLEGAL.sub("", text)
text = re.sub(r"\s+", " ", text).strip(" .")
return text or "download"
def rel_path(meta: MediaMeta, ext: str, layout: str = "plex") -> str:
"""Path of the media file relative to DOWNLOAD_ROOT (forward slashes)."""
title = sanitize(meta.title)