Files

226 lines
7.2 KiB
Python

"""Tests for the movie object and the pool layout it derives (src.core.movie)."""
from pathlib import Path
from typing import Any
import pytest
from src.core.movie import (
DEFAULT_VERSION_LABEL,
MediaFile,
Movie,
identity_key,
media_filename,
normalize_3d_label,
)
@pytest.mark.parametrize(
"label,expected",
[
("HSBS", "HSBS"),
("hsbs", "HSBS"),
("3D HSBS", "HSBS"),
("3d.hsbs", "HSBS"),
("3D_FTAB", "FTAB"),
("3d-mvc", "MVC"),
("4K", None),
("Director's Cut", None),
("", None),
(None, None),
],
)
def test_normalize_3d_label(label, expected):
assert normalize_3d_label(label) == expected
class TestMediaFilename:
def test_no_label_is_the_bare_stem(self):
assert (
media_filename("Matrix (1999)", "", ".mkv", grouped=False)
== "Matrix (1999).mkv"
)
def test_3d_label_uses_jellyfins_documented_form(self):
assert (
media_filename("Avatar (2009)", "3D HSBS", ".mkv", grouped=False)
== "Avatar (2009).3D.HSBS.mkv"
)
# same spelling grouped — "." is a valid separator after the folder name
assert (
media_filename("Avatar (2009)", "hsbs", ".mkv", grouped=True)
== "Avatar (2009).3D.HSBS.mkv"
)
def test_plain_label_uses_dash_form(self):
assert (
media_filename("Avatar (2009)", "4K", ".mkv", grouped=True)
== "Avatar (2009) - 4K.mkv"
)
def test_grouped_empty_label_gets_a_default(self):
assert (
media_filename("Avatar (2009)", "", ".mkv", grouped=True)
== f"Avatar (2009) - {DEFAULT_VERSION_LABEL}.mkv"
)
class TestIdentityKey:
def test_tmdb_id_wins(self):
assert identity_key(603, "Matrix", "1999") == "tmdb-603"
assert identity_key("603", "cokoli", None) == "tmdb-603"
def test_slug_fallback_without_id(self):
assert identity_key(None, "Matrix", "1999") == "name-matrix-1999"
def test_slug_folds_diacritics(self):
assert identity_key(None, "Pelíšky", "1999") == "name-pelisky-1999"
def test_slug_without_year(self):
assert identity_key(None, "Nějaký film", None) == "name-nejaky-film"
def _movie(**kwargs: Any) -> Movie:
base: dict[str, Any] = {
"key": "tmdb-19995",
"title": "Avatar",
"year": "2009",
"tmdb_id": 19995,
"library": "Filmy",
}
base.update(kwargs)
return Movie(**base)
class TestMovieShape:
def test_stem_is_the_canonical_form(self):
assert _movie().stem == "Avatar (2009) [tmdbid-19995]"
def test_stem_falls_back_to_title_without_year(self):
assert _movie(year=None, tmdb_id=None).stem == "Avatar"
def test_single_video_is_not_grouped(self):
m = _movie(media=[MediaFile(Path("/pool/Filmy/x.mkv"))])
assert m.is_grouped is False
def test_two_videos_are_grouped(self):
m = _movie(
media=[
MediaFile(Path("/a.mkv"), "3D HSBS"),
MediaFile(Path("/b.mkv"), "Výchozí"),
]
)
assert m.is_grouped is True
def test_primary_prefers_the_plain_version(self):
m = _movie(
media=[MediaFile(Path("/a.mkv"), "3D HSBS"), MediaFile(Path("/b.mkv"), "")]
)
assert m.primary.label == ""
class TestDesiredLayout:
def test_one_video_stays_flat_in_the_library(self, tmp_path):
m = _movie(media=[MediaFile(tmp_path / "kdekoli.mkv", "")])
((_media, target),) = m.desired_layout(tmp_path)
assert target == tmp_path / "Filmy" / "Avatar (2009) [tmdbid-19995].mkv"
def test_one_3d_video_keeps_the_3d_tag_flat(self, tmp_path):
m = _movie(media=[MediaFile(tmp_path / "x.mkv", "3D HSBS")])
((_m, target),) = m.desired_layout(tmp_path)
assert target.name == "Avatar (2009) [tmdbid-19995].3D.HSBS.mkv"
assert target.parent == tmp_path / "Filmy"
def test_two_videos_move_into_the_movie_folder(self, tmp_path):
m = _movie(
media=[
MediaFile(tmp_path / "a.mkv", ""),
MediaFile(tmp_path / "b.mkv", "3D HSBS"),
]
)
targets = {mf.label: path for mf, path in m.desired_layout(tmp_path)}
folder = tmp_path / "Filmy" / "Avatar (2009) [tmdbid-19995]"
assert (
targets[""]
== folder / f"Avatar (2009) [tmdbid-19995] - {DEFAULT_VERSION_LABEL}.mkv"
)
assert targets["3D HSBS"] == folder / "Avatar (2009) [tmdbid-19995].3D.HSBS.mkv"
def test_layout_follows_a_retitle(self, tmp_path):
"""Renaming the movie is just a different desired layout — the thing
that used to be impossible for a grouped movie."""
m = _movie(
title="Avatar: Nová verze",
media=[
MediaFile(tmp_path / "a.mkv", "A"),
MediaFile(tmp_path / "b.mkv", "B"),
],
)
folders = {path.parent for _mf, path in m.desired_layout(tmp_path)}
assert folders == {
tmp_path / "Filmy" / "Avatar: Nová verze (2009) [tmdbid-19995]"
}
def test_layout_follows_a_library_move(self, tmp_path):
m = _movie(library="Kids", media=[MediaFile(tmp_path / "a.mkv", "")])
((_m, target),) = m.desired_layout(tmp_path)
assert target.parent == tmp_path / "Kids"
class TestRecordRoundTrip:
def test_to_and_from_record(self, tmp_path):
m = _movie(
media=[MediaFile(tmp_path / "Filmy" / "a.mkv", "3D HSBS")],
csfd_url="https://www.csfd.cz/film/1/",
date="2026-01-01",
attributes={"collection_sort": "01"},
meta={"year": "2009"},
)
record = m.to_record(tmp_path)
assert record["media"] == [
{"role": "video", "label": "3D HSBS", "file": "Filmy/a.mkv"}
]
back = Movie.from_record("tmdb-19995", record, tmp_path)
assert back.title == "Avatar"
assert back.year == "2009"
assert back.tmdb_id == 19995
assert back.library == "Filmy"
assert back.csfd_url == "https://www.csfd.cz/film/1/"
assert back.attributes == {"collection_sort": "01"}
assert back.meta == {"year": "2009"}
assert back.media[0].path == tmp_path / "Filmy" / "a.mkv"
assert back.media[0].format_3d == "HSBS"
def test_from_record_builds_tags_via_tagmanager(self, tmp_path):
from src.core.tag_manager import TagManager
record = {"title": "Avatar", "media": [], "tags": ["Žánr/Sci-Fi"]}
back = Movie.from_record("tmdb-19995", record, tmp_path, TagManager())
assert {t.full_path for t in back.tags} == {"Žánr/Sci-Fi"}
@pytest.mark.parametrize(
"label,expected",
[
("part1", 1),
("Part 2", 2),
("pt3", 3),
("CD1", 1),
("disc-2", 2),
("dvd_4", 4),
("disk12", 12),
("4K", None),
("3D HSBS", None),
("", None),
("partition", None),
],
)
def test_normalize_part_label(label, expected):
from src.core.movie import normalize_part_label
assert normalize_part_label(label) == expected