Filter NAS photos by EXIF-aware orientation with persistent dimension cache
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
"""EXIF-aware dimensions without decoding original photo pixels."""
|
||||
|
||||
import os
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
|
||||
|
||||
def dimensions(root: Path, path: Path):
|
||||
parent_fd = os.open(root, os.O_RDONLY | os.O_DIRECTORY)
|
||||
try:
|
||||
parts = path.relative_to(root).parts
|
||||
for part in parts[:-1]:
|
||||
child = os.open(
|
||||
part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=parent_fd
|
||||
)
|
||||
os.close(parent_fd)
|
||||
parent_fd = child
|
||||
fd = os.open(
|
||||
parts[-1], os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=parent_fd
|
||||
)
|
||||
with os.fdopen(fd, "rb") as stream:
|
||||
if not stat.S_ISREG(os.fstat(stream.fileno()).st_mode):
|
||||
return None
|
||||
with Image.open(stream) as image:
|
||||
width, height = image.size
|
||||
if image.getexif().get(274, 1) in (5, 6, 7, 8):
|
||||
width, height = height, width
|
||||
return width, height
|
||||
except (OSError, ValueError, UnidentifiedImageError, Image.DecompressionBombError):
|
||||
return None
|
||||
finally:
|
||||
os.close(parent_fd)
|
||||
|
||||
|
||||
def orientation(width, height):
|
||||
return "landscape" if width > height else "portrait" if height > width else "square"
|
||||
Reference in New Issue
Block a user