iPhone file classification by filename pattern

iOS helper: classify_iphone_file() groups photos by name pattern:
- Camera: IMG_XXXX.{HEIC,JPG} — camera originals
- Edited: IMG_EXXXX.{HEIC,JPG} — Photos.app edits
- Videos: IMG_XXXX.MOV and other video files
- Screenshots: any .PNG (iPhone camera never produces PNG)
- Screen Recordings: RPReplay_Final*.mp4
- Other: everything else (web saves, app exports, etc.)

ExifFilter.isCameraRollFolder: Camera and Edited are camera-roll,
Screenshots/Screen Recordings/Other are not.

sourceFolder now reflects these groups instead of raw DCIM folder names
(145APPLE etc.) so the left panel shows meaningful categories.
This commit is contained in:
Kyle Bolen
2026-07-30 04:19:28 +00:00
parent 716124bff5
commit e6d27d3ab3
2 changed files with 39 additions and 3 deletions

View File

@@ -20,6 +20,8 @@ import os
import asyncio
from datetime import datetime
import re
MEDIA_EXTENSIONS = {
'jpg', 'jpeg', 'heic', 'heif', 'avif', 'webp', 'png',
'dng', 'raw', 'arw', 'cr2', 'cr3', 'nef', 'orf', 'rw2',
@@ -31,6 +33,35 @@ VIDEO_EXTENSIONS = {
'mp4', 'm4v', 'mov', 'mpeg', 'mpg', '3gp', '3g2', 'avi', 'mkv', 'webm',
}
# iPhone camera originals: IMG_XXXX.{HEIC,JPG,MOV} — exactly 4 digits
CAMERA_ORIGINAL_RE = re.compile(r'^IMG_\d{4}\.(HEIC|HEIF|JPG|JPEG|MOV)$', re.IGNORECASE)
# Edited versions: IMG_EXXXX — edited in Photos.app
CAMERA_EDITED_RE = re.compile(r'^IMG_E\d{4}\.(HEIC|HEIF|JPG|JPEG)$', re.IGNORECASE)
# Screenshots: PNG files (iPhone camera never produces PNG)
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:
"""
Returns a group name for display in the folder panel.
Groups: 'Camera', 'Edited', 'Screenshots', 'Videos', 'Other'
"""
ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''
if SCREEN_RECORDING_RE.match(filename):
return 'Screen Recordings'
if SCREENSHOT_RE.match(filename):
return 'Screenshots'
if CAMERA_ORIGINAL_RE.match(filename):
if ext in ('mov',):
return 'Videos'
return 'Camera'
if CAMERA_EDITED_RE.match(filename):
return 'Edited'
if ext in VIDEO_EXTENSIONS:
return 'Videos'
return 'Other'
async def get_lockdown():
from pymobiledevice3.lockdown import create_using_usbmux
@@ -110,7 +141,7 @@ async def cmd_list_photos():
'filename': filename,
'sizeBytes': size_bytes,
'dateIso': date_iso,
'sourceFolder': folder,
'sourceFolder': classify_iphone_file(filename),
'mediaType': 'VIDEO' if ext in VIDEO_EXTENSIONS else 'PHOTO',
'isHeic': ext in ('heic', 'heif'),
})