"""Tests for series naming/scanning (src.core.series) + FileManager episodes.""" import pytest from src.core.series import ( collect_episode_files, episode_filename, is_canonical_episode, is_season_folder, parse_episode, sanitize_title, scan_series, season_folder_name, series_folder_stem, ) @pytest.mark.parametrize("name,expected", [ ("Breaking Bad S01E02.mkv", (1, 2)), ("bb.s1e2.mkv", (1, 2)), ("Show 01x02 x264.mkv", (1, 2)), ("Series.S01E02E03.mkv", (1, 2)), # multi-ep → first ("Series S12E145.mkv", (12, 145)), ("No code here.mkv", None), ]) def test_parse_episode(name, expected): assert parse_episode(name) == expected def test_season_folder_name(): assert season_folder_name(0) == "Specials" assert season_folder_name(1) == "Season 01" assert season_folder_name(12) == "Season 12" def test_episode_filename_is_code_plus_series_title(): assert episode_filename(1, 2, "Most!", ".mkv") == "S01E02 - Most!.mkv" assert episode_filename(0, 3, "Most!", ".mkv") == "S00E03 - Most!.mkv" # release string is NOT kept — only the code + series title assert episode_filename(12, 145, "Breaking Bad", ".mkv") == \ "S12E145 - Breaking Bad.mkv" # empty / unusable title → just the code assert episode_filename(3, 4, "", ".mkv") == "S03E04.mkv" assert episode_filename(3, 4, "/", ".mkv") == "S03E04.mkv" def test_sanitize_title(): assert sanitize_title("Most!") == "Most!" assert sanitize_title('A: B / C D') == "A B C D" assert sanitize_title(" trailing. ") == "trailing" def test_is_season_folder(): assert is_season_folder("Season 01") assert is_season_folder("Season 1") assert is_season_folder("Specials") assert not is_season_folder("Extras") assert not is_season_folder("Season") def test_series_folder_stem(): assert series_folder_stem("Breaking Bad", 2008, 1396) == "Breaking Bad (2008) [tmdbid-1396]" assert series_folder_stem("Neznámý", None, 42) == "Neznámý [tmdbid-42]" assert series_folder_stem("Jen název", None, None) == "Jen název" def test_is_canonical_episode(tmp_path): folder = tmp_path / "Breaking Bad (2008) [tmdbid-1396]" / "Season 01" folder.mkdir(parents=True) good = folder / "S01E02 - Breaking Bad.mkv" good.write_bytes(b"x") bad = folder / "bb.s01e02.mkv" bad.write_bytes(b"x") wrong_body = folder / "S01E02 - old release.mkv" wrong_body.write_bytes(b"x") assert is_canonical_episode(good, "Breaking Bad") assert not is_canonical_episode(bad, "Breaking Bad") assert not is_canonical_episode(wrong_body, "Breaking Bad") def test_scan_series(tmp_path): lib = tmp_path / "Seriály" s1 = lib / "Breaking Bad (2008) [tmdbid-1396]" / "Season 01" s1.mkdir(parents=True) (s1 / "Breaking Bad S01E01.mkv").write_bytes(b"x") (s1 / "Breaking Bad S01E02.mkv").write_bytes(b"x") (s1 / "poster.jpg").write_bytes(b"img") # sidecar → skipped found = scan_series(lib, "Seriály") assert len(found) == 1 ser = found[0] assert ser.title == "Breaking Bad" and ser.year == "2008" and ser.tmdb_id == 1396 assert ser.library == "Seriály" assert len(ser.episodes) == 2 seasons = ser.seasons() assert list(seasons.keys()) == [1] and len(seasons[1]) == 2 def test_collect_episode_files_walks_subfolders(tmp_path): root = tmp_path / "Breaking.Bad.COMPLETE" (root / "Season 01").mkdir(parents=True) (root / "Season 02").mkdir(parents=True) (root / "Season 01" / "BB S01E01.mkv").write_bytes(b"x") (root / "Season 01" / "BB S01E02.mkv").write_bytes(b"x") (root / "Season 01" / "BB S01E02.srt").write_bytes(b"x") # sidecar → skipped (root / "Season 02" / "BB S02E01.avi").write_bytes(b"x") (root / "readme.nfo").write_bytes(b"x") # sidecar → skipped files = collect_episode_files(root) assert [f.name for f in files] == [ "BB S01E01.mkv", "BB S01E02.mkv", "BB S02E01.avi"] def test_collect_episode_files_missing_folder_is_empty(tmp_path): assert collect_episode_files(tmp_path / "nope") == [] class TestFileManagerSeries: @pytest.fixture def file_manager(self, tmp_path, monkeypatch): import src.core.config as config_module monkeypatch.setattr(config_module, "GLOBAL_CONFIG_FILE", tmp_path / ".gtag") from src.core.tag_manager import TagManager from src.core.file_manager import FileManager fm = FileManager(TagManager()) fm.set_pool_dir(tmp_path / "pool") return fm def test_import_episodes_places_them(self, file_manager, tmp_path): for n in ("bb.S01E01.mkv", "bb.S01E02.mkv", "notes.txt_S00.dummy"): (tmp_path / n).write_bytes(b"x") result = file_manager.import_episodes( [tmp_path / "bb.S01E01.mkv", tmp_path / "bb.S01E02.mkv"], "Breaking Bad", year=2008, tmdb_id=1396) assert result["imported"] == 2 base = (tmp_path / "pool" / "Seriály" / "Breaking Bad (2008) [tmdbid-1396]" / "Season 01") assert (base / "S01E01 - Breaking Bad.mkv").exists() assert (base / "S01E02 - Breaking Bad.mkv").exists() def test_import_episodes_into_mixed_library(self, file_manager, tmp_path): file_manager.set_libraries([{"name": "Hlavní", "kind": "mixed"}]) (tmp_path / "bb.S01E01.mkv").write_bytes(b"x") result = file_manager.import_episodes( [tmp_path / "bb.S01E01.mkv"], "Breaking Bad", year=2008, tmdb_id=1396, library="Hlavní") assert result["imported"] == 1 assert (tmp_path / "pool" / "Hlavní" / "Breaking Bad (2008) [tmdbid-1396]" / "Season 01" / "S01E01 - Breaking Bad.mkv").exists() # a mixed library is scanned for series too found = file_manager.load_series() assert [s.title for s in found] == ["Breaking Bad"] def test_import_episodes_skips_uncoded(self, file_manager, tmp_path): (tmp_path / "random.mkv").write_bytes(b"x") result = file_manager.import_episodes( [tmp_path / "random.mkv"], "Breaking Bad", year=2008) assert result["imported"] == 0 assert result["skipped_no_code"] == ["random.mkv"] def test_remove_series_deletes_the_folder(self, file_manager, tmp_path): (tmp_path / "bb.S01E01.mkv").write_bytes(b"x") (tmp_path / "bb.S02E01.mkv").write_bytes(b"x") file_manager.import_episodes( [tmp_path / "bb.S01E01.mkv", tmp_path / "bb.S02E01.mkv"], "Breaking Bad", year=2008, tmdb_id=1396) (ser,) = file_manager.load_series() assert ser.folder.is_dir() file_manager.remove_series(ser) assert not ser.folder.exists() assert file_manager.load_series() == [] def test_remove_series_refuses_folder_outside_pool(self, file_manager, tmp_path): from src.core.series import Series outside = tmp_path / "not-the-pool" / "Show (2020)" outside.mkdir(parents=True) bogus = Series(outside, "Show", "2020", None, "Seriály") with pytest.raises(ValueError): file_manager.remove_series(bogus) assert outside.is_dir() # untouched def test_import_episodes_uses_explicit_codes_override(self, file_manager, tmp_path): uncoded = tmp_path / "pilot.mkv" uncoded.write_bytes(b"x") misparsed = tmp_path / "bb 2008 e07.mkv" # "2008" would parse as 20x08 misparsed.write_bytes(b"x") result = file_manager.import_episodes( [uncoded, misparsed], "Breaking Bad", year=2008, tmdb_id=1396, codes={uncoded: (1, 1), misparsed: (1, 7)}) assert result["imported"] == 2 base = (tmp_path / "pool" / "Seriály" / "Breaking Bad (2008) [tmdbid-1396]" / "Season 01") assert (base / "S01E01 - Breaking Bad.mkv").exists() assert (base / "S01E07 - Breaking Bad.mkv").exists() assert result["skipped_no_code"] == [] def test_rename_episodes_to_canonical(self, file_manager, tmp_path): # a mis-named episode sitting in the wrong place folder = (tmp_path / "pool" / "Seriály" / "Breaking Bad (2008) [tmdbid-1396]") folder.mkdir(parents=True) (folder / "bb.1x03.HDTV.mkv").write_bytes(b"x") series_list = file_manager.load_series() assert len(series_list) == 1 result = file_manager.rename_episodes_to_canonical(series_list[0]) assert result["renamed"] == [("bb.1x03.HDTV.mkv", "S01E03 - Breaking Bad.mkv")] assert (folder / "Season 01" / "S01E03 - Breaking Bad.mkv").exists() def test_rename_episodes_to_canonical_carries_subtitle_along( self, file_manager, tmp_path ): folder = (tmp_path / "pool" / "Seriály" / "Breaking Bad (2008) [tmdbid-1396]") folder.mkdir(parents=True) (folder / "bb.1x03.HDTV.mkv").write_bytes(b"x") (folder / "bb.1x03.HDTV.cs.srt").write_bytes(b"x") result = file_manager.rename_episodes_to_canonical(file_manager.load_series()[0]) assert result["renamed"] == [("bb.1x03.HDTV.mkv", "S01E03 - Breaking Bad.mkv")] assert (folder / "Season 01" / "S01E03 - Breaking Bad.cs.srt").exists() assert not (folder / "bb.1x03.HDTV.cs.srt").exists() def test_rename_all_series_to_canonical_bulk(self, file_manager, tmp_path): seri = tmp_path / "pool" / "Seriály" # series A: mis-named episodes in a non-padded season folder (seri / "Fargo (2014) [tmdbid-60622]" / "Season 1").mkdir(parents=True) (seri / "Fargo (2014) [tmdbid-60622]" / "Season 1" / "fargo.1x01.1080p.WEB.mkv").write_bytes(b"x") # series B: one good, one without a code (seri / "Most! (2019) [tmdbid-90000]" / "Season 01").mkdir(parents=True) (seri / "Most! (2019) [tmdbid-90000]" / "Season 01" / "S01E02 - Most!.mkv").write_bytes(b"x") (seri / "Most! (2019) [tmdbid-90000]" / "Season 01" / "necoZvlastniho.mkv").write_bytes(b"x") result = file_manager.rename_all_series_to_canonical() assert result["series"] == 2 assert ("Fargo", "fargo.1x01.1080p.WEB.mkv", "S01E01 - Fargo.mkv") in result["renamed"] assert (seri / "Fargo (2014) [tmdbid-60622]" / "Season 01" / "S01E01 - Fargo.mkv").exists() # emptied non-padded season folder swept assert not (seri / "Fargo (2014) [tmdbid-60622]" / "Season 1").exists() # the already-canonical one is untouched, the code-less one is reported assert ("Most!", "necoZvlastniho.mkv") in result["skipped_no_code"] assert (seri / "Most! (2019) [tmdbid-90000]" / "Season 01" / "S01E02 - Most!.mkv").exists() def test_apply_series_edits_renames_folder_and_episodes(self, file_manager, tmp_path): folder = tmp_path / "pool" / "Seriály" / "Wrong Name" (folder / "Season 01").mkdir(parents=True) (folder / "Season 01" / "old S01E01.mkv").write_bytes(b"x") ser = file_manager.load_series()[0] result = file_manager.apply_series_edits( ser, title="Breaking Bad", year=2008, tmdb_id=1396) new_folder = (tmp_path / "pool" / "Seriály" / "Breaking Bad (2008) [tmdbid-1396]") assert result["folder"] == new_folder assert (new_folder / "Season 01" / "S01E01 - Breaking Bad.mkv").exists() assert not folder.exists()