Detect Live Photo companions and show video file size
iOS helper: classify_iphone_file now detects Live Photo MOV companions by checking if IMG_XXXX.MOV has a matching IMG_XXXX.HEIC/JPG in the same DCIM folder. Falls back to size heuristic (<5MB = Live Photo). Live Photo companions appear in 'Live Photo' group (Other section) — excluded from camera-roll default selection. DenseThumb: show file size on video badge (e.g. '▶ 2MB' or '▶ 42MB') so Live Photo clips (small) are visually distinct from real videos (large).
This commit is contained in:
@@ -42,10 +42,13 @@ SCREENSHOT_RE = re.compile(r'.*\.PNG$', re.IGNORECASE)
|
||||
# Screen recordings
|
||||
SCREEN_RECORDING_RE = re.compile(r'^RPReplay_Final.*\.mp4$', re.IGNORECASE)
|
||||
|
||||
def classify_iphone_file(filename: str) -> str:
|
||||
def classify_iphone_file(filename: str, all_filenames: set = None, size_bytes: int = 0) -> str:
|
||||
"""
|
||||
Returns a group name for display in the folder panel.
|
||||
Groups: 'Camera', 'Edited', 'Screenshots', 'Videos', 'Other'
|
||||
Groups: 'Camera', 'Edited', 'Live Photo', 'Screenshots', 'Videos', 'Other'
|
||||
|
||||
all_filenames: set of all filenames in the same DCIM folder (for pairing detection).
|
||||
size_bytes: used to distinguish Live Photo clips (<5MB) from real videos.
|
||||
"""
|
||||
ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''
|
||||
if SCREEN_RECORDING_RE.match(filename):
|
||||
@@ -54,7 +57,19 @@ def classify_iphone_file(filename: str) -> str:
|
||||
return 'Screenshots'
|
||||
if CAMERA_ORIGINAL_RE.match(filename):
|
||||
if ext in ('mov',):
|
||||
return 'Videos'
|
||||
# Check if this is a Live Photo companion
|
||||
base = filename.rsplit('.', 1)[0] # IMG_XXXX
|
||||
is_live_companion = False
|
||||
if all_filenames is not None:
|
||||
# Paired HEIC/JPEG exists with same base name → Live Photo companion
|
||||
for still_ext in ('HEIC', 'HEIF', 'JPG', 'JPEG'):
|
||||
if f'{base}.{still_ext}' in all_filenames:
|
||||
is_live_companion = True
|
||||
break
|
||||
elif size_bytes > 0 and size_bytes < 5 * 1024 * 1024:
|
||||
# No pairing info but very small MOV → likely Live Photo
|
||||
is_live_companion = True
|
||||
return 'Live Photo' if is_live_companion else 'Videos'
|
||||
return 'Camera'
|
||||
if CAMERA_EDITED_RE.match(filename):
|
||||
return 'Edited'
|
||||
@@ -109,6 +124,9 @@ async def cmd_list_photos():
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Build a set of all filenames in this folder for Live Photo pairing
|
||||
all_filenames_in_folder = set(entries)
|
||||
|
||||
for filename in entries:
|
||||
ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''
|
||||
if ext not in MEDIA_EXTENSIONS:
|
||||
@@ -141,7 +159,7 @@ async def cmd_list_photos():
|
||||
'filename': filename,
|
||||
'sizeBytes': size_bytes,
|
||||
'dateIso': date_iso,
|
||||
'sourceFolder': classify_iphone_file(filename),
|
||||
'sourceFolder': classify_iphone_file(filename, all_filenames_in_folder, size_bytes),
|
||||
'mediaType': 'VIDEO' if ext in VIDEO_EXTENSIONS else 'PHOTO',
|
||||
'isHeic': ext in ('heic', 'heif'),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user