-
Notifications
You must be signed in to change notification settings - Fork 0
/
pypixpro–code
168 lines (148 loc) · 6.53 KB
/
pypixpro–code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/env python3
import os
import sys
import shutil
import subprocess
from pathlib import Path
import hashlib
from PIL import Image, ExifTags, UnidentifiedImageError
try:
import pyheif # For HEIC files
except ImportError as e:
subprocess.run(['osascript', '-e', f'display alert "Error: pyheif not found. Install it using pip."'])
sys.exit(1)
# Constants
STANDARD_IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".psd",
".svg", ".ico", ".jfif", ".pjpeg", ".pjp", ".avif", ".apng"}
PNG_EXTENSION = ".png"
HEIC_EXTENSIONS = {".heic", ".heif"}
DNG_RAW_EXTENSIONS = {".dng", ".raw", ".nef", ".cr2", ".cr3", ".arw", ".orf", ".rw2", ".raf", ".srw", ".kdc"}
MISC_EXTENSIONS = {".doc", ".docx", ".txt", ".py", ".zsh", ".sh", ".mov", ".mp4", ".pdf", ".xlsx", ".pptx"}
# Folder Names
PORTRAIT_FOLDER_NAME = "Portrait"
LANDSCAPE_FOLDER_NAME = "Landscape"
SCREENSHOTS_FOLDER_NAME = "Screenshots"
MISC_FOLDER_NAME = "Misc"
def get_input_folder():
"""Get the folder path from command-line arguments or show a prompt."""
if len(sys.argv) > 1:
input_folder_path = sys.argv[1]
return Path(input_folder_path)
else:
prompt_message = (
"⚠️ Please back up your files before processing them.\n\n"
"Drag and drop a folder onto the droplet to begin."
)
subprocess.run(['osascript', '-e', f'display alert "{prompt_message}"'])
sys.exit(1)
def get_prefix(folder_name):
"""Prompt for a prefix for Portrait and Landscape folders."""
try:
result = subprocess.run(
['osascript', '-e', f'display dialog "Enter prefix for {folder_name} photos:" default answer ""'],
capture_output=True, text=True, check=True
)
output = result.stdout.strip().split("text returned:")[-1].strip()
return output or f"{folder_name} Photo"
except subprocess.CalledProcessError:
return f"{folder_name} Photo"
def generate_checksums(root_folder):
"""Generate file checksums using hashlib."""
checksums = {}
for path in root_folder.rglob('*'):
if path.is_file():
try:
hasher = hashlib.sha256()
with path.open('rb') as f:
while chunk := f.read(8192):
hasher.update(chunk)
checksum = hasher.hexdigest()
checksums.setdefault(checksum, []).append(path)
except Exception:
pass # Continue on error
return checksums
def correct_orientation(image):
"""Correct image orientation using EXIF metadata."""
try:
exif = image._getexif()
if exif is not None:
orientation = exif.get(274) # EXIF tag 274 is Orientation
if orientation == 3:
image = image.rotate(180, expand=True)
elif orientation == 6:
image = image.rotate(270, expand=True)
elif orientation == 8:
image = image.rotate(90, expand=True)
return image
except Exception as e:
print(f"Error correcting orientation: {e}")
return image # Return original image on error
def rename_files(target_folder, prefix):
"""Rename files with the given prefix."""
files = sorted(target_folder.glob('*'))
for idx, file in enumerate(files, 1):
new_name = f"{prefix} {str(idx).zfill(3)}{file.suffix}"
try:
file.rename(target_folder / new_name)
except Exception:
pass # Continue on error
def process_heic_image(path):
"""Process HEIC image to determine its dimensions."""
try:
heif_file = pyheif.read(path)
width, height = heif_file.size
print(f"Processed HEIC: {path.name} - {width}x{height}") # Debugging dimensions
return width, height
except Exception as e:
print(f"Error processing HEIC {path}: {e}")
return None, None
def sort_images(root_folder, portrait_prefix, landscape_prefix):
"""Sort images into folders and apply prefixes."""
portrait_folder = root_folder / PORTRAIT_FOLDER_NAME
landscape_folder = root_folder / LANDSCAPE_FOLDER_NAME
portrait_folder.mkdir(exist_ok=True)
landscape_folder.mkdir(exist_ok=True)
screenshots_folder = None
misc_folder = None
for path in root_folder.rglob('*'):
if path.is_file() and path.parent not in {portrait_folder, landscape_folder}:
suffix = path.suffix.lower()
try:
if suffix in HEIC_EXTENSIONS:
width, height = process_heic_image(path)
if width and height:
target = portrait_folder if height > width else landscape_folder
shutil.move(str(path), target / path.name)
elif suffix in STANDARD_IMAGE_EXTENSIONS:
with Image.open(path) as img:
img = correct_orientation(img) # Correct orientation for JPEGs
width, height = img.size
print(f"Processed JPEG: {path.name} - {width}x{height}") # Debugging dimensions
target = portrait_folder if height > width else landscape_folder
shutil.move(str(path), target / path.name)
elif suffix == PNG_EXTENSION:
if not screenshots_folder:
screenshots_folder = root_folder / SCREENSHOTS_FOLDER_NAME
screenshots_folder.mkdir(exist_ok=True)
shutil.move(str(path), screenshots_folder / path.name)
elif suffix in DNG_RAW_EXTENSIONS:
shutil.move(str(path), landscape_folder / path.name)
elif suffix in MISC_EXTENSIONS:
if not misc_folder:
misc_folder = root_folder / MISC_FOLDER_NAME
misc_folder.mkdir(exist_ok=True)
shutil.move(str(path), misc_folder / path.name)
except Exception as e:
print(f"Error processing {path}: {e}")
rename_files(portrait_folder, portrait_prefix)
rename_files(landscape_folder, landscape_prefix)
def main():
"""Main entry point."""
input_folder = get_input_folder()
checksums = generate_checksums(input_folder)
portrait_prefix = get_prefix("Portrait")
landscape_prefix = get_prefix("Landscape")
sort_images(input_folder, portrait_prefix, landscape_prefix)
subprocess.run(['osascript', '-e', 'display alert "Process completed successfully!"'])
if __name__ == "__main__":
main()