From 5ac78654d4d754575e5b54b7f6f10b7485a78033 Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Sun, 26 Jul 2026 22:11:05 -0500 Subject: [PATCH] Code review fix: Library Cleanup left orphaned rows on episode delete Fixing a missing-file or duplicate-import episode only deleted the episode itself, leaving its episode_progress/subtitle rows behind for the next scan to catch as separate orphaned_subtitles/dangling findings. Consolidated movie/episode/track deletion (with all their dependent rows) into one _delete_content() helper, used consistently by missing_files, orphaned_episodes, and duplicate_paths. --- backend/library_cleanup.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/backend/library_cleanup.py b/backend/library_cleanup.py index 461a46d..37d57f5 100644 --- a/backend/library_cleanup.py +++ b/backend/library_cleanup.py @@ -105,22 +105,33 @@ async def scan(db, videos_dir: Path, hls_dir: Path) -> Dict[str, List[Dict]]: return findings +async def _delete_content(db, content_type: str, item_id: str) -> None: + """Deletes a movie/episode/track and every row that references it, so a Fix never + leaves orphans behind for the next scan to catch on a second pass.""" + if content_type == "movie": + await db.movies.delete_one({"id": item_id}) + await db.watchlist.delete_many({"movie_id": item_id}) + await db.progress.delete_many({"movie_id": item_id}) + await db.subtitles.delete_many({"movie_id": item_id}) + elif content_type == "episode": + await db.episodes.delete_one({"id": item_id}) + await db.episode_progress.delete_many({"episode_id": item_id}) + await db.subtitles.delete_many({"episode_id": item_id}) + elif content_type == "track": + await db.tracks.delete_one({"id": item_id}) + else: + raise ValueError(f"Unknown content_type: {content_type}") + + async def fix_one(db, videos_dir: Path, hls_dir: Path, category: str, item: Dict) -> None: content_type = item.get("content_type") item_id = item.get("id") if category == "missing_files": - coll = {"movie": db.movies, "episode": db.episodes, "track": db.tracks}[content_type] - await coll.delete_one({"id": item_id}) - if content_type == "movie": - await db.watchlist.delete_many({"movie_id": item_id}) - await db.progress.delete_many({"movie_id": item_id}) - await db.subtitles.delete_many({"movie_id": item_id}) + await _delete_content(db, content_type, item_id) elif category == "orphaned_episodes": - await db.episodes.delete_one({"id": item_id}) - await db.episode_progress.delete_many({"episode_id": item_id}) - await db.subtitles.delete_many({"episode_id": item_id}) + await _delete_content(db, "episode", item_id) elif category == "orphaned_hls": shutil.rmtree(hls_dir / item_id, ignore_errors=True) @@ -132,8 +143,7 @@ async def fix_one(db, videos_dir: Path, hls_dir: Path, category: str, item: Dict }}) elif category == "duplicate_paths": - coll = {"movie": db.movies, "episode": db.episodes, "track": db.tracks}[content_type] - await coll.delete_one({"id": item_id}) + await _delete_content(db, content_type, item_id) elif category == "orphaned_subtitles": sub = await db.subtitles.find_one({"id": item_id}, {"_id": 0})