diff --git a/src/core/SerieScanner.py b/src/core/SerieScanner.py index b690c11..6e45d90 100644 --- a/src/core/SerieScanner.py +++ b/src/core/SerieScanner.py @@ -27,6 +27,7 @@ from src.core.providers.base_provider import Loader from src.server.database.connection import get_sync_session from src.server.database.service import AnimeSeriesService, EpisodeService +from src.core.utils.key_utils import generate_key_from_folder logger = logging.getLogger(__name__) error_logger = logging.getLogger("error") @@ -708,6 +709,31 @@ class SerieScanner: ) return Serie.load_from_file(serie_file) + # Step 4: Generate key from folder name as last resort + # This handles edge cases like non-Latin characters or special symbols + try: + generated_key = generate_key_from_folder(folder_name) + year_from_folder = self._extract_year_from_folder_name(folder_name) + logger.info( + "Generated key for folder '%s' -> key='%s'", + folder_name, + generated_key + ) + return Serie( + key=generated_key, + name="", # Name will be fetched from provider if needed + site="aniworld.to", + folder=folder_name, + episodeDict=dict(), + year=year_from_folder + ) + except Exception as exc: + logger.warning( + "Failed to generate key for folder '%s': %s", + folder_name, + exc + ) + return None def __get_episode_and_season(self, filename: str) -> tuple[int, int]: diff --git a/src/server/services/anime_service.py b/src/server/services/anime_service.py index 65f7813..af9d82c 100644 --- a/src/server/services/anime_service.py +++ b/src/server/services/anime_service.py @@ -528,6 +528,8 @@ class AnimeService: "tmdb_id": db_series.tmdb_id, "tvdb_id": db_series.tvdb_id, "series_id": db_series.id, + "loading_status": db_series.loading_status, + "loading_error": db_series.loading_error, } # Build episodeDict from DB, skipping is_downloaded=True @@ -596,6 +598,8 @@ class AnimeService: "tmdb_id": nfo_data.get("tmdb_id"), "tvdb_id": nfo_data.get("tvdb_id"), "series_id": nfo_data.get("series_id"), + "loading_status": nfo_data.get("loading_status"), + "loading_error": nfo_data.get("loading_error"), } result_list.append(series_dict) diff --git a/tests/unit/test_serie_scanner.py b/tests/unit/test_serie_scanner.py index 6c019d7..2ad5dff 100644 --- a/tests/unit/test_serie_scanner.py +++ b/tests/unit/test_serie_scanner.py @@ -552,8 +552,8 @@ class TestReadDataFromFile: assert result is not None assert result.key == "test-key" - def test_no_files_returns_none(self, mock_loader): - """Should return None when no key or data file exists.""" + def test_no_files_returns_serie_with_generated_key(self, mock_loader): + """Should return Serie with generated key when no key or data file exists.""" import tempfile with tempfile.TemporaryDirectory() as tmpdir: @@ -562,7 +562,10 @@ class TestReadDataFromFile: scanner = SerieScanner(tmpdir, mock_loader) result = scanner._SerieScanner__read_data_from_file("Empty") - assert result is None + # Step 4 generates key from folder name when no files exist + assert result is not None + assert isinstance(result, Serie) + assert result.key == "empty" class TestReinit: @@ -763,7 +766,7 @@ class TestDbLookupFallback: assert scanner.keyDict["rooster-fighter"].episodeDict == {1: [1, 2, 3]} def test_db_lookup_returns_none_folder_skipped(self, mock_loader): - """When db_lookup returns None, the folder is skipped with a warning.""" + """When db_lookup returns None, Step 4 fallback generates key from folder name.""" import tempfile with tempfile.TemporaryDirectory() as tmp_dir: @@ -773,10 +776,11 @@ class TestDbLookupFallback: with patch.object(scanner, 'get_total_to_scan', return_value=1): scanner.scan() - assert len(scanner.keyDict) == 0 + # Step 4 generates key from folder name, so keyDict is not empty + assert len(scanner.keyDict) == 1 def test_db_lookup_exception_skips_folder(self, mock_loader): - """When db_lookup raises, the folder is skipped gracefully.""" + """When db_lookup raises, Step 4 fallback generates key from folder name.""" import tempfile with tempfile.TemporaryDirectory() as tmp_dir: @@ -786,7 +790,8 @@ class TestDbLookupFallback: with patch.object(scanner, 'get_total_to_scan', return_value=1): scanner.scan() # should not raise - assert len(scanner.keyDict) == 0 + # Step 4 generates key from folder name, so keyDict is not empty + assert len(scanner.keyDict) == 1 def test_db_lookup_warning_logged_when_no_files( self, mock_loader, caplog