fix(downloads): keep sidecar basenames intact when the title contains a dot
write_sidecars / delete_asset_files computed `base = media.with_suffix("")` and then
`base.with_suffix(".nfo")`. Stripping the real `.mp4` first leaves a stem whose own last dot may
be inside the title (e.g. "Sonka Házilag."), which `with_suffix` then treats as the extension and
clobbers — writing the .nfo/.jpg under a TRUNCATED basename that no longer matches the media, so
Plex/Kodi never associate them (and delete_asset_files targets the wrong name, leaking orphans).
Swap the extension on the FULL media path via a shared `_sidecar` helper instead.
Pre-existing bug (predates the source-adapters work); found during its E2E and folded in here.
Verified end-to-end: a dotted-title Facebook reel now writes a single shared basename.
This commit is contained in:
@@ -181,16 +181,23 @@ def _nfo_xml(meta: MediaMeta) -> str:
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def _sidecar(media: Path, ext: str) -> Path:
|
||||
"""The sibling sidecar path (`.nfo` / `.jpg`) for a media file. Swaps the extension on the FULL
|
||||
media path — NOT via `media.with_suffix("")` first: stripping the real extension leaves a stem
|
||||
whose OWN last dot may be inside the title (e.g. "Sonka Házilag."), which `with_suffix` would
|
||||
then read as the extension and clobber, orphaning the sidecar under a truncated name."""
|
||||
return media.with_suffix(ext)
|
||||
|
||||
|
||||
def write_sidecars(
|
||||
download_root: str, rel: str, meta: MediaMeta, thumb_src: Path | None
|
||||
) -> bool:
|
||||
"""Write `<basename>.nfo` + `<basename>.jpg` next to the media file. Returns True if written."""
|
||||
media = abs_path(download_root, rel)
|
||||
base = media.with_suffix("")
|
||||
try:
|
||||
base.with_suffix(".nfo").write_text(_nfo_xml(meta), encoding="utf-8")
|
||||
_sidecar(media, ".nfo").write_text(_nfo_xml(meta), encoding="utf-8")
|
||||
if thumb_src and thumb_src.exists():
|
||||
shutil.copyfile(str(thumb_src), str(base.with_suffix(".jpg")))
|
||||
shutil.copyfile(str(thumb_src), str(_sidecar(media, ".jpg")))
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
@@ -199,8 +206,7 @@ def write_sidecars(
|
||||
def delete_asset_files(download_root: str, rel: str) -> None:
|
||||
"""Remove the media file and its sidecars; prune now-empty parent dirs (best effort)."""
|
||||
media = abs_path(download_root, rel)
|
||||
base = media.with_suffix("")
|
||||
for p in (media, base.with_suffix(".nfo"), base.with_suffix(".jpg")):
|
||||
for p in (media, _sidecar(media, ".nfo"), _sidecar(media, ".jpg")):
|
||||
try:
|
||||
p.unlink()
|
||||
except OSError:
|
||||
|
||||
@@ -147,3 +147,26 @@ def test_nfo_uniqueid_type_reflects_the_source():
|
||||
base = dict(video_id="v1", title="t", uploader="c", upload_date=None)
|
||||
assert '<uniqueid type="youtube"' in _nfo_xml(MediaMeta(**base)) # default preserved
|
||||
assert '<uniqueid type="facebook"' in _nfo_xml(MediaMeta(**base, uniqueid_type="facebook"))
|
||||
|
||||
|
||||
def test_sidecars_keep_the_full_basename_when_the_title_has_a_dot(tmp_path: Path):
|
||||
# A title ending in a period ("Sonka Házilag.") puts a dot in the media slug; the .nfo/.jpg
|
||||
# sidecars must sit on the SAME (full) basename, not be truncated at that dot (which orphaned
|
||||
# them from the media so Plex/Kodi never matched them).
|
||||
from app.downloads.storage import write_sidecars
|
||||
|
||||
stem = "Chan_-_2026-08-12_-_Sonka_Hazilag._Villam_konyha_abc_a5"
|
||||
rel = f"Chan/Season_2026/{stem}.mp4"
|
||||
media = tmp_path / rel
|
||||
media.parent.mkdir(parents=True)
|
||||
media.write_text("x")
|
||||
thumb = tmp_path / "t.jpg"
|
||||
thumb.write_bytes(b"\xff\xd8\xff")
|
||||
meta = MediaMeta(video_id="abc", title="Sonka Házilag. | Villám konyha", uploader="Chan",
|
||||
upload_date=None)
|
||||
|
||||
assert write_sidecars(str(tmp_path), rel, meta, thumb) is True
|
||||
names = sorted(p.name for p in media.parent.iterdir())
|
||||
assert f"{stem}.nfo" in names, names
|
||||
assert f"{stem}.jpg" in names, names
|
||||
assert "Chan_-_2026-08-12_-_Sonka_Hazilag.nfo" not in names # the old truncated orphan
|
||||
|
||||
Reference in New Issue
Block a user