fix(rescan): delete downloaded episode rows when no longer missing
A finished download marks the Episode row with is_downloaded=True and
populates file_path (commit 0ba2587). The intent was to preserve
download history, but the row stayed in the DB forever because two
sibling sync methods refused to delete it:
- AnimeService._update_series_in_db: `downloaded_set` guard skipped
deletion if the row was marked downloaded.
- SerieScanner._sync_episodes_to_db: `if ep.is_downloaded: continue`
kept downloaded rows.
The user-visible bug: the missing-list UI is correct (it filters by
is_downloaded and the broadcast rebuilds from the DB), but a finished
download left a stale entry in the DB that the user could see when
querying the database directly. Worse, this stale row accumulated
indefinitely across rescans.
Fix: drop the is_downloaded preservation guards. Once the scanner
confirms the file is on disk and the episode is no longer in the
missing set, the Episode row has no further purpose and is deleted to
keep the DB in sync with the filesystem. The UI derives "missing"
from row presence, so deleting the row is the correct way to make the
episode stop appearing as missing in *all* views (UI and DB queries).
Tests:
- test_update_series_deletes_downloaded_episodes_when_no_longer_missing
(RED): regression for _update_series_in_db.
- test_update_series_keeps_still_missing_episodes: sanity sibling
to ensure the fix does not over-reach and delete still-missing
episodes.
- test_deletes_downloaded_episodes_when_no_longer_missing
(RED): regression for SerieScanner._sync_episodes_to_db.
- Replaced test_preserves_downloaded_episodes (which asserted the
old buggy behavior) with the deletion-asserting variant.
This commit is contained in:
@@ -101,12 +101,20 @@ class TestSyncEpisodesToDb:
|
||||
"""Test _sync_episodes_to_db method."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_preserves_downloaded_episodes(self):
|
||||
"""Verify downloaded episodes are not removed even when no longer missing."""
|
||||
async def test_deletes_downloaded_episodes_when_no_longer_missing(self):
|
||||
"""Downloaded episodes are deleted once the rescan confirms the
|
||||
file is on disk and they are no longer missing. The DB stays
|
||||
in sync with the filesystem: a row that is not in the scanner's
|
||||
missing set has no further purpose and is removed.
|
||||
"""
|
||||
mock_session = AsyncMock()
|
||||
|
||||
# S01E1 was downloaded (file exists), S01E2 was missing but file now exists
|
||||
# Both are no longer in episode_dict
|
||||
# S01E1 was downloaded (file exists) and the scanner confirms
|
||||
# it is no longer missing; S01E2 was previously marked as
|
||||
# downloaded and is also no longer missing. Both should be
|
||||
# deleted — there is no notion of "preserving download history"
|
||||
# in the DB: the rescan's filesystem view is the source of
|
||||
# truth, and a row whose episode is no longer missing is dead.
|
||||
existing_eps = [
|
||||
MagicMock(id=1, season=1, episode_number=1, is_downloaded=True),
|
||||
MagicMock(id=2, season=1, episode_number=2, is_downloaded=True),
|
||||
@@ -126,8 +134,16 @@ class TestSyncEpisodesToDb:
|
||||
mock_session, 1, {} # No episodes missing
|
||||
)
|
||||
|
||||
# Neither should be deleted since both are downloaded
|
||||
mock_delete.assert_not_called()
|
||||
# Both downloaded rows should be deleted; the scanner
|
||||
# found the files on disk and they're not in the
|
||||
# missing set.
|
||||
assert mock_delete.call_count == 2
|
||||
deleted_calls = [
|
||||
(c.args[1], c.args[2], c.args[3])
|
||||
for c in mock_delete.call_args_list
|
||||
]
|
||||
assert (1, 1, 1) in deleted_calls
|
||||
assert (1, 1, 2) in deleted_calls
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_removes_missing_episodes_when_no_longer_missing(self):
|
||||
|
||||
Reference in New Issue
Block a user