Add NFO file support to Serie and SerieList entities
- Add nfo_path property to Serie class - Add has_nfo(), has_poster(), has_logo(), has_fanart() methods - Update to_dict()/from_dict() to include nfo metadata - Modify SerieList.load_series() to detect NFO and media files - Add logging for missing NFO and media files with statistics - Comprehensive unit tests with 100% coverage - All 67 tests passing
This commit is contained in:
@@ -294,3 +294,228 @@ class TestSerieListBackwardCompatibility:
|
||||
assert serie_list.contains(sample_serie.key)
|
||||
loaded = serie_list.get_by_key(sample_serie.key)
|
||||
assert loaded.name == sample_serie.name
|
||||
|
||||
|
||||
class TestSerieListNFOFeatures:
|
||||
"""Test SerieList NFO detection and logging."""
|
||||
|
||||
def test_load_series_detects_nfo_file(self, temp_directory, caplog):
|
||||
"""Test load_series detects and sets nfo_path for series with NFO."""
|
||||
import logging
|
||||
caplog.set_level(logging.INFO)
|
||||
|
||||
# Create series folder with data file and NFO
|
||||
folder_name = "Test Series"
|
||||
folder_path = os.path.join(temp_directory, folder_name)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
serie = Serie(
|
||||
key="test-series",
|
||||
name="Test Series",
|
||||
site="https://example.com",
|
||||
folder=folder_name,
|
||||
episodeDict={1: [1, 2]}
|
||||
)
|
||||
|
||||
data_path = os.path.join(folder_path, "data")
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
serie.save_to_file(data_path)
|
||||
|
||||
# Create NFO file
|
||||
nfo_path = os.path.join(folder_path, "tvshow.nfo")
|
||||
with open(nfo_path, "w") as f:
|
||||
f.write("<tvshow></tvshow>")
|
||||
|
||||
# Load series
|
||||
serie_list = SerieList(temp_directory)
|
||||
|
||||
# Verify NFO was detected
|
||||
loaded = serie_list.get_by_key("test-series")
|
||||
assert loaded is not None
|
||||
assert loaded.nfo_path == nfo_path
|
||||
|
||||
# Verify logging
|
||||
assert "1 with NFO" in caplog.text
|
||||
|
||||
def test_load_series_detects_missing_nfo(self, temp_directory, caplog):
|
||||
"""Test load_series logs when NFO is missing."""
|
||||
import logging
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
||||
# Create series folder with data file but NO NFO
|
||||
folder_name = "Test Series"
|
||||
folder_path = os.path.join(temp_directory, folder_name)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
serie = Serie(
|
||||
key="test-series",
|
||||
name="Test Series",
|
||||
site="https://example.com",
|
||||
folder=folder_name,
|
||||
episodeDict={1: [1, 2]}
|
||||
)
|
||||
|
||||
data_path = os.path.join(folder_path, "data")
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
serie.save_to_file(data_path)
|
||||
|
||||
# Load series
|
||||
serie_list = SerieList(temp_directory)
|
||||
|
||||
# Verify NFO not set
|
||||
loaded = serie_list.get_by_key("test-series")
|
||||
assert loaded is not None
|
||||
assert loaded.nfo_path is None
|
||||
|
||||
# Verify logging
|
||||
assert "missing tvshow.nfo" in caplog.text
|
||||
|
||||
def test_load_series_detects_media_files(self, temp_directory, caplog):
|
||||
"""Test load_series detects poster, logo, and fanart files."""
|
||||
import logging
|
||||
caplog.set_level(logging.INFO)
|
||||
|
||||
# Create series folder with all media files
|
||||
folder_name = "Test Series"
|
||||
folder_path = os.path.join(temp_directory, folder_name)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
serie = Serie(
|
||||
key="test-series",
|
||||
name="Test Series",
|
||||
site="https://example.com",
|
||||
folder=folder_name,
|
||||
episodeDict={1: [1, 2]}
|
||||
)
|
||||
|
||||
data_path = os.path.join(folder_path, "data")
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
serie.save_to_file(data_path)
|
||||
|
||||
# Create media files
|
||||
with open(os.path.join(folder_path, "poster.jpg"), "w") as f:
|
||||
f.write("poster data")
|
||||
with open(os.path.join(folder_path, "logo.png"), "w") as f:
|
||||
f.write("logo data")
|
||||
with open(os.path.join(folder_path, "fanart.jpg"), "w") as f:
|
||||
f.write("fanart data")
|
||||
|
||||
# Load series
|
||||
serie_list = SerieList(temp_directory)
|
||||
|
||||
# Verify logging shows all media found
|
||||
assert "Poster (1/1)" in caplog.text
|
||||
assert "Logo (1/1)" in caplog.text
|
||||
assert "Fanart (1/1)" in caplog.text
|
||||
|
||||
def test_load_series_detects_missing_media_files(
|
||||
self, temp_directory, caplog
|
||||
):
|
||||
"""Test load_series logs when media files are missing."""
|
||||
import logging
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
||||
# Create series folder with NO media files
|
||||
folder_name = "Test Series"
|
||||
folder_path = os.path.join(temp_directory, folder_name)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
serie = Serie(
|
||||
key="test-series",
|
||||
name="Test Series",
|
||||
site="https://example.com",
|
||||
folder=folder_name,
|
||||
episodeDict={1: [1, 2]}
|
||||
)
|
||||
|
||||
data_path = os.path.join(folder_path, "data")
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
serie.save_to_file(data_path)
|
||||
|
||||
# Load series
|
||||
serie_list = SerieList(temp_directory)
|
||||
|
||||
# Verify logging shows missing media
|
||||
assert "missing poster.jpg" in caplog.text
|
||||
assert "missing logo.png" in caplog.text
|
||||
assert "missing fanart.jpg" in caplog.text
|
||||
|
||||
def test_load_series_summary_statistics(self, temp_directory, caplog):
|
||||
"""Test load_series logs summary statistics for NFO and media."""
|
||||
import logging
|
||||
caplog.set_level(logging.INFO)
|
||||
|
||||
# Create multiple series with varying NFO/media status
|
||||
for i in range(3):
|
||||
folder_name = f"Series {i}"
|
||||
folder_path = os.path.join(temp_directory, folder_name)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
serie = Serie(
|
||||
key=f"series-{i}",
|
||||
name=f"Series {i}",
|
||||
site="https://example.com",
|
||||
folder=folder_name,
|
||||
episodeDict={1: [1]}
|
||||
)
|
||||
|
||||
data_path = os.path.join(folder_path, "data")
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
serie.save_to_file(data_path)
|
||||
|
||||
# First series has everything
|
||||
if i == 0:
|
||||
with open(os.path.join(folder_path, "tvshow.nfo"), "w") as f:
|
||||
f.write("<tvshow></tvshow>")
|
||||
with open(os.path.join(folder_path, "poster.jpg"), "w") as f:
|
||||
f.write("poster")
|
||||
with open(os.path.join(folder_path, "logo.png"), "w") as f:
|
||||
f.write("logo")
|
||||
with open(os.path.join(folder_path, "fanart.jpg"), "w") as f:
|
||||
f.write("fanart")
|
||||
# Second series has NFO and poster only
|
||||
elif i == 1:
|
||||
with open(os.path.join(folder_path, "tvshow.nfo"), "w") as f:
|
||||
f.write("<tvshow></tvshow>")
|
||||
with open(os.path.join(folder_path, "poster.jpg"), "w") as f:
|
||||
f.write("poster")
|
||||
# Third series has nothing
|
||||
|
||||
# Load series
|
||||
serie_list = SerieList(temp_directory)
|
||||
|
||||
# Verify summary statistics
|
||||
assert "3 series total" in caplog.text
|
||||
assert "2 with NFO, 1 without NFO" in caplog.text
|
||||
assert "Poster (2/3)" in caplog.text
|
||||
assert "Logo (1/3)" in caplog.text
|
||||
assert "Fanart (1/3)" in caplog.text
|
||||
|
||||
def test_load_series_handles_load_failure(self, temp_directory, caplog):
|
||||
"""Test load_series handles series that fail to load gracefully."""
|
||||
import logging
|
||||
caplog.set_level(logging.ERROR)
|
||||
|
||||
# Create folder with invalid data file
|
||||
folder_name = "Invalid Series"
|
||||
folder_path = os.path.join(temp_directory, folder_name)
|
||||
os.makedirs(folder_path)
|
||||
|
||||
data_path = os.path.join(folder_path, "data")
|
||||
with open(data_path, "w") as f:
|
||||
f.write("invalid json {{{")
|
||||
|
||||
# Load series - should not crash
|
||||
serie_list = SerieList(temp_directory)
|
||||
|
||||
# Verify error logged
|
||||
assert "Failed to load metadata" in caplog.text
|
||||
|
||||
# Should not be in keyDict
|
||||
assert len(serie_list.keyDict) == 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user