fix: deduplicate scanner-created source_roots and folders
Two related fixes:
1. Prevention — scanner now normalizes paths before lookup/insert
in get_or_create_source_root and get_or_create_folder. Trailing
slashes, redundant separators, and `.` segments all collapse to
the same row. _normalize_path uses os.path.normpath; symlinks
are intentionally NOT resolved so mount paths stay intact for
cross-machine portability.
2. Cleanup — new app/services/cleanup.py runs on backend startup
(idempotent) and merges any pre-existing duplicates left over
from older scanner versions:
- Groups source_roots by normalized path. Picks the canonical
row (preferring one with a non-empty name and the earliest
added_at), re-points child Folder rows via UPDATE, and
deletes the duplicates.
- Same for folders, with photo_count as the tiebreaker. Photos
get re-pointed to the canonical folder via UPDATE.
- Recomputes folder.photo_count from the actual non-discarded
photo membership so the sidebar count matches reality.
Wired into main.py's lifespan handler. On the dev DB this merged
the empty-name "/host/Pictures/MulitaTest/" duplicate that was
showing up alongside the canonical MulitaTest source root.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ from app.config import settings
|
||||
from app.database import init_db
|
||||
from app.routers import photos, folders, heaps, tags, discard, library
|
||||
from app.services.scanner import start_initial_scan
|
||||
from app.services.cleanup import cleanup_data_integrity
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
@@ -25,17 +26,24 @@ logger = logging.getLogger(__name__)
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Manage application lifecycle"""
|
||||
logger.info("Starting Mulita application...")
|
||||
|
||||
|
||||
# Initialize database
|
||||
await init_db()
|
||||
|
||||
|
||||
# One-shot cleanup of duplicate source_roots / folders left over from
|
||||
# earlier scanner versions that didn't normalize paths. Idempotent.
|
||||
try:
|
||||
await cleanup_data_integrity()
|
||||
except Exception as e:
|
||||
logger.error(f"Startup cleanup failed (continuing): {e}")
|
||||
|
||||
# Start initial scan if configured
|
||||
if settings.scanner.initial_scan_on_start:
|
||||
logger.info("Starting initial library scan...")
|
||||
await start_initial_scan()
|
||||
|
||||
|
||||
yield
|
||||
|
||||
|
||||
logger.info("Shutting down Mulita application...")
|
||||
|
||||
# Create FastAPI app
|
||||
|
||||
Reference in New Issue
Block a user