feat: Enhance tag management in TagSelectedPhotosDialog with improved logic for removable tags

This commit updates the TagSelectedPhotosDialog to allow both single and bulk tags to be managed more effectively. The logic for determining removable tags has been refined, ensuring that users can see which tags are common across selected photos and whether they can be removed. Additionally, the photo date extraction method in PhotoManager has been improved to include a fallback to file modification time, enhancing reliability. The photo service now also utilizes this updated method for date extraction, ensuring consistency across the application. Documentation has been updated to reflect these changes.
This commit is contained in:
tanyar09
2025-11-11 14:35:50 -05:00
parent f7accb925d
commit 52344febad
3 changed files with 116 additions and 55 deletions
+23 -8
View File
@@ -23,16 +23,22 @@ class PhotoManager:
self.verbose = verbose
def extract_photo_date(self, photo_path: str) -> Optional[str]:
"""Extract date taken from photo EXIF data"""
"""Extract date taken from photo with fallback to file modification time.
Tries in order:
1. EXIF date tags (DateTimeOriginal, DateTimeDigitized, DateTime)
2. File modification time (as fallback)
"""
# First try EXIF date extraction
try:
with Image.open(photo_path) as image:
exifdata = image.getexif()
# Look for date taken in EXIF tags
date_tags = [
306, # DateTime
36867, # DateTimeOriginal
36868, # DateTimeDigitized
36867, # DateTimeOriginal - when photo was actually taken (highest priority)
36868, # DateTimeDigitized - when photo was digitized
306, # DateTime - file modification date (lowest priority)
]
for tag_id in date_tags:
@@ -50,12 +56,21 @@ class PhotoManager:
return date_obj.strftime('%Y-%m-%d')
except ValueError:
continue
return None
except Exception as e:
if self.verbose >= 2:
print(f" ⚠️ Could not extract date from {os.path.basename(photo_path)}: {e}")
return None
print(f" ⚠️ Could not extract EXIF date from {os.path.basename(photo_path)}: {e}")
# Fallback to file modification time
try:
if os.path.exists(photo_path):
mtime = os.path.getmtime(photo_path)
mtime_date = datetime.fromtimestamp(mtime)
return mtime_date.strftime('%Y-%m-%d')
except Exception as e:
if self.verbose >= 2:
print(f" ⚠️ Could not get file modification time from {os.path.basename(photo_path)}: {e}")
return None
def scan_folder(self, folder_path: str, recursive: bool = True) -> int:
"""Scan folder for photos and add to database"""
+39 -2
View File
@@ -102,6 +102,36 @@ def extract_exif_date(image_path: str) -> Optional[date]:
return None
def extract_photo_date(image_path: str) -> Optional[date]:
"""Extract date taken from photo with fallback to file modification time.
Tries in order:
1. EXIF date tags (DateTimeOriginal, DateTimeDigitized, DateTime)
2. File modification time (as fallback)
Returns:
Date object or None if no date can be determined
"""
# First try EXIF date extraction
date_taken = extract_exif_date(image_path)
if date_taken:
return date_taken
# Fallback to file modification time
try:
if os.path.exists(image_path):
mtime = os.path.getmtime(image_path)
mtime_date = datetime.fromtimestamp(mtime).date()
return mtime_date
except Exception as e:
# Log error for debugging (but don't fail the import)
import logging
logger = logging.getLogger(__name__)
logger.debug(f"Failed to get file modification time from {image_path}: {e}")
return None
def find_photos_in_folder(folder_path: str, recursive: bool = True) -> list[str]:
"""Find all photo files in a folder."""
folder_path = os.path.abspath(folder_path)
@@ -142,10 +172,17 @@ def import_photo_from_path(
# Check if photo already exists by path
existing = db.query(Photo).filter(Photo.path == photo_path).first()
if existing:
# If existing photo doesn't have date_taken, try to update it
if existing.date_taken is None:
date_taken = extract_photo_date(photo_path)
if date_taken:
existing.date_taken = date_taken
db.commit()
db.refresh(existing)
return existing, False
# Extract date taken (returns Date to match desktop schema)
date_taken = extract_exif_date(photo_path)
# Extract date taken with fallback to file modification time
date_taken = extract_photo_date(photo_path)
# Create new photo record - match desktop schema exactly
# Desktop schema: id, path, filename, date_added, date_taken (DATE), processed