Add path canonicalization to prevent symlink duplicates
Implements centralized path canonicalization to ensure symlinks and paths with . or .. segments are resolved to their canonical form, preventing false duplicate detection. Changes to utils.py: - Add canonical_path() function using Path.resolve() - Add canonical_path_str() for dictionary key usage Changes to models.py: - Add __post_init__ to VideoFile to auto-canonicalize paths - All VideoFile instances now have canonical paths automatically Impact: - Path-based dictionary lookups now work correctly with symlinks - io.py: path_to_inventory dictionary uses canonical keys - planner.py: path_to_index dictionary uses canonical keys - analysis.py: Duplicate detection uses canonical paths - No false duplicates from symlinks or relative paths Testing: - Added comprehensive tests for canonical_path functions - Tests verify symlink resolution, absolute path conversion, .. removal - All 449 tests pass (added 10 new tests) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
parent
d6c8852e1e
commit
e3069a287e
+8
-3
@@ -13,9 +13,9 @@ from typing import Optional
|
||||
@dataclass
|
||||
class VideoFile:
|
||||
"""Represents a video file discovered during inventory scanning.
|
||||
|
||||
|
||||
Attributes:
|
||||
path: Full path to the video file
|
||||
path: Full path to the video file (automatically canonicalized)
|
||||
filename: Name of the file (without directory path)
|
||||
size_bytes: File size in bytes
|
||||
modified_timestamp: Last modification timestamp
|
||||
@@ -30,13 +30,18 @@ class VideoFile:
|
||||
size_bytes: int
|
||||
modified_timestamp: datetime
|
||||
category: str
|
||||
|
||||
|
||||
# Optional metadata (if ffprobe available)
|
||||
resolution: Optional[str] = None
|
||||
codec: Optional[str] = None
|
||||
duration_seconds: Optional[float] = None
|
||||
bitrate_kbps: Optional[int] = None
|
||||
|
||||
def __post_init__(self):
|
||||
"""Canonicalize path on creation."""
|
||||
from vlm.utils import canonical_path
|
||||
object.__setattr__(self, 'path', canonical_path(self.path))
|
||||
|
||||
|
||||
@dataclass
|
||||
class MovieIdentity:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Shared utilities for Video Library Manager."""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def utc_now() -> datetime:
|
||||
@@ -22,3 +23,27 @@ def format_size(size_bytes: int) -> str:
|
||||
return f"{size_bytes:.1f} {unit}"
|
||||
size_bytes /= 1024.0
|
||||
return f"{size_bytes:.1f} PB"
|
||||
|
||||
|
||||
def canonical_path(path: Path) -> Path:
|
||||
"""Return canonical absolute path, resolving symlinks.
|
||||
|
||||
Args:
|
||||
path: Path to canonicalize
|
||||
|
||||
Returns:
|
||||
Canonical absolute path with symlinks resolved
|
||||
"""
|
||||
return path.resolve()
|
||||
|
||||
|
||||
def canonical_path_str(path: Path) -> str:
|
||||
"""Return canonical path as string for use as dictionary key.
|
||||
|
||||
Args:
|
||||
path: Path to canonicalize
|
||||
|
||||
Returns:
|
||||
String representation of canonical path
|
||||
"""
|
||||
return str(canonical_path(path))
|
||||
|
||||
Reference in New Issue
Block a user