156 lines
6.2 KiB
Python
156 lines
6.2 KiB
Python
"""Tests for the movie object and the pool layout it derives (src.core.movie)."""
|
|
from pathlib import Path
|
|
|
|
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) -> Movie:
|
|
base = dict(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"}
|