init image

This commit is contained in:
2024-07-13 16:23:11 +02:00
commit 1183327e7e
13 changed files with 890 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/config.enc
/__pycache__
*/__pycache__
/build
/dist
/temp

202
api/woocommerce.py Normal file
View File

@@ -0,0 +1,202 @@
from cryptography.fernet import Fernet
import json
import os
import requests
import base64
from woocommerce import API
from tkinter import messagebox
import tempfile
from utils.image_processing import resize_image
import pprint
credentials_file = 'credentials.json'
# Hardcoded key (replace with your generated key)
key = b'u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI='
def save_credentials(url, consumer_key, consumer_secret, username, password):
credentials = {
'url': url,
'consumer_key': consumer_key,
'consumer_secret': consumer_secret,
'username': username,
'password': password
}
credentials_str = json.dumps(credentials)
fernet = Fernet(key)
encrypted = fernet.encrypt(credentials_str.encode())
with open('config.enc', 'wb') as file:
file.write(encrypted)
def load_credentials():
if not os.path.exists('config.enc'):
return None
fernet = Fernet(key)
with open('config.enc', 'rb') as file:
encrypted = file.read()
decrypted = fernet.decrypt(encrypted).decode()
return json.loads(decrypted)
def get_wcapi():
credentials = load_credentials()
if not credentials:
messagebox.showerror("Error", "No WooCommerce credentials found. Please set them in the settings.")
return None
return API(
url=credentials['url'],
consumer_key=credentials['consumer_key'],
consumer_secret=credentials['consumer_secret'],
version="wc/v3"
)
def get_product(id):
wcapi = get_wcapi()
if not wcapi:
return None
result = wcapi.get("products/"+str(id))
image_paths = {}
product = result.json()
if product.get('images'):
images = product.get('images')
if not os.path.exists('temp'):
os.makedirs('temp')
for index, image in enumerate(images):
image_url = image.get('src')
image_id = image.get('id')
response = requests.get(image_url)
if response.status_code == 200:
file_name = image_url.split('/')[-1]
file_path = os.path.join('temp', file_name)
image_paths[image_id] = file_path
with open(file_path, 'wb') as file:
file.write(response.content)
print(f"Image {index + 1}/{len(images)} downloaded and saved: {file_path}")
else:
print(f"Failed to download image {index + 1}/{len(images)}")
else:
if product.get('name'):
print(f"No images found for {product.get('name')}")
else:
print("No images found")
return image_paths, product
def upload_image(imgPath):
data = open(imgPath, 'rb').read()
fileName = os.path.basename(imgPath)
credentials = load_credentials()
if not credentials:
messagebox.showerror("Error", "No WordPress credentials found. Please set them in the settings.")
return None
username = credentials['username']
password = credentials['password']
credentials_base64 = base64.b64encode(f"{username}:{password}".encode())
credentials_base64 = base64.b64encode(f"{username}:{password}".encode())
url = f"{credentials['url']}/wp-json/wp/v2/media"
headers = {
'Content-Type': 'image/jpg',
'Content-Disposition': f'attachment; filename={fileName}',
'Authorization': f'basic {credentials_base64.decode()}'
}
try:
res = requests.post(url=url, data=data, headers=headers)
res.raise_for_status() # Raise an HTTPError if the HTTP request returned an unsuccessful status code
newDict = res.json()
newID = newDict.get('id')
link = newDict.get('guid').get("rendered") if newDict.get('guid') else None
print(newID, link)
return newID if newID else False
except requests.exceptions.RequestException as e:
print(f"Error uploading image: {e}")
return False
def delete_img(image_id):
credentials = load_credentials()
if not credentials:
messagebox.showerror("Error", "No WordPress credentials found. Please set them in the settings.")
return None
url = f"{credentials['url']}/wp-json/wp/v2/media/{image_id}"
username = credentials['username']
password = credentials['password']
credentials_base64 = base64.b64encode(f"{username}:{password}".encode())
res = requests.delete(url=url,
headers={'Authorization': f'basic {credentials_base64.decode()}'},
params={'force': 'true'})
if res.status_code == 200:
print(f"Image with ID {image_id} deleted successfully.")
else:
print(f"Failed to delete image with ID {image_id}. Error: {res.text}")
def update_product(image_ids, old_image_ids, product_id):
wcapi = get_wcapi()
if not wcapi:
return
product = wcapi.get(f"products/{product_id}").json()
product['images'] = [{'id': image_id} for image_id in image_ids]
response = wcapi.put(f"products/{product_id}", data=product)
if response.status_code == 200:
print(f"Product with ID {product_id} updated successfully with new image IDs.")
else:
print(f"Failed to update product with ID {product_id}. Error: {response.text}")
def process_product_images(id, name_template, canvas_width, canvas_height):
print(name_template)
image_paths, product = get_product(id)
if not image_paths:
return
with tempfile.TemporaryDirectory() as temp_output_directory:
print(f"Using temporary directory: {temp_output_directory}")
old_list = []
new_list = []
for image_id, file_path in image_paths.items():
output_path = generate_output_path(temp_output_directory, file_path, name_template, product, canvas_width, canvas_height)
resize_image(file_path, output_path, '')
new_id = upload_image(output_path)
if new_id:
old_list.append(image_id)
new_list.append(new_id)
update_product(new_list, old_list, id)
print("Temporary files processed and uploaded successfully.")
def generate_output_path(temp_output_directory, file_path, template, product, canvas_width, canvas_height):
# Generate the new filename based on the template
name, ext = os.path.splitext(os.path.basename(file_path))
width = canvas_width
height = canvas_height
sku = product.get('sku', '')
slug = product.get('name', '')
title = product.get('slug', '')
pprint.pprint(product)
# Here you can add more attributes to the template if needed
new_filename = template.format(name=name, sku=sku, width=width, height=height, slug=slug, title=title)
return os.path.join(temp_output_directory, new_filename + ext)
def process_all_products(name_template):
wcapi = get_wcapi()
if not wcapi:
return
page = 1
while True:
products = wcapi.get("products", params={"per_page": 100, "page": page}).json()
if not products:
break
for product in products:
process_product_images(product['id'], name_template)
page += 1
messagebox.showinfo("Process Complete", "All product images processing is complete.")

25
decrypt_config.py Normal file
View File

@@ -0,0 +1,25 @@
from cryptography.fernet import Fernet
import json
import os
# Hardcoded key (replace with your generated key)
key = b'u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI='
def decrypt_config(key):
if not os.path.exists("config.enc"):
raise FileNotFoundError("The encrypted configuration file 'config.enc' does not exist.")
fernet = Fernet(key)
with open("config.enc", "rb") as encrypted_file:
encrypted = encrypted_file.read()
decrypted = fernet.decrypt(encrypted).decode()
return json.loads(decrypted)
if __name__ == "__main__":
try:
config = decrypt_config(key)
print(config) # Use the decrypted config
except FileNotFoundError as e:
print(e)
except Exception as e:
print(f"An error occurred: {e}")

22
encrypt_config.py Normal file
View File

@@ -0,0 +1,22 @@
from cryptography.fernet import Fernet
# Generate a key and print it
key = Fernet.generate_key()
print(f"Encryption key: {key.decode()}") # Save this key securely
# Function to encrypt the configuration data
def encrypt_config(data, key):
fernet = Fernet(key)
encrypted = fernet.encrypt(data.encode())
with open("config.enc", "wb") as encrypted_file:
encrypted_file.write(encrypted)
if __name__ == "__main__":
config_data = """{
"url": "https://yourstore.com",
"consumer_key": "ck_yourconsumerkey",
"consumer_secret": "cs_yoursecret",
"username": "yourusername",
"password": "yourpassword"
}"""
encrypt_config(config_data, key)

38
image_processor.spec Normal file
View File

@@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='image_processor',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

47
main.py Normal file
View File

@@ -0,0 +1,47 @@
import tkinter as tk
from tkinter import ttk
from ui.local_processing_tab import create_tab_local
from ui.settings_tab import create_tab_settings
from ui.log_window import LogWindow
from decrypt_config import decrypt_config, key
try:
config = decrypt_config(key)
wc_url = config['url']
wc_consumer_key = config['consumer_key']
wc_consumer_secret = config['consumer_secret']
wp_username = config['username']
wp_password = config['password']
# Now use these variables to create your WooCommerce API instance
except FileNotFoundError as e:
print(e)
# Handle the missing file case, e.g., by prompting the user to create it
except Exception as e:
print(f"An error occurred: {e}")
# Handle other potential errors
log_window = None
def open_log_window():
global log_window
if log_window is None or not log_window.winfo_exists():
log_window = LogWindow(window)
else:
log_window.lift()
if __name__ == "__main__":
window = tk.Tk()
window.title("Image Processor")
window.geometry("700x400")
tab_parent = ttk.Notebook(window)
create_tab_local(tab_parent, "Local Processing", open_log_window)
create_tab_settings(tab_parent, "Settings")
tab_parent.pack(expand=True, fill='both')
window.mainloop()

38
main.spec Normal file
View File

@@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

135
readme.md Normal file
View File

@@ -0,0 +1,135 @@
Image Processing Application
This application is a graphical user interface (GUI) tool designed for processing images either from a local directory or from a WooCommerce product catalog. It supports resizing images, renaming them based on customizable templates, and uploading them to a WooCommerce site.
Features
Resize images to specified dimensions.
Rename images using customizable templates with placeholders for various attributes.
Upload processed images to WooCommerce.
Process images from a local directory or WooCommerce products.
Prerequisites
Python 3.10 or later
Required Python Packages
You can install the required packages using pip:
sh
pip install -r requirements.txt
ImageMagick
ImageMagick is required for image processing. You can download and install it from ImageMagick's official website.
WooCommerce API Credentials
You need to have WooCommerce API credentials to interact with the WooCommerce site. These include the URL, consumer key, consumer secret, username, and password.
Setting Up
Clone the Repository
Clone this repository to your local machine:
sh
git clone https://github.com/your-username/image-processing-app.git
cd image-processing-app
Install Dependencies
Install the required Python packages:
sh
pip install -r requirements.txt
Configure WooCommerce API Credentials
Run the application and navigate to the Settings tab to enter and save your WooCommerce API credentials.
Running the Application
Run the main application script:
sh
python main.py
Creating an Executable
To create a standalone executable from this project, you can use PyInstaller. Follow the steps below:
Install PyInstaller
You can install PyInstaller using pip:
sh
pip install pyinstaller
Generate the Executable
Navigate to the project directory and run PyInstaller:
sh
pyinstaller --onefile --noconsole main.py
Find the Executable
After PyInstaller finishes, you can find the executable in the dist directory.
File Structure
main.py: Entry point for the application.
api/: Contains the woocommerce.py file for interacting with WooCommerce API.
utils/: Contains utility scripts for file operations and image processing.
ui/: Contains the UI components for the application.
config.enc: Encrypted file storing WooCommerce API credentials.
Requirements
Create a requirements.txt file with the following content:
tk
Pillow
requests
woocommerce
cryptography
pyinstaller
Usage
Select Source Type
Choose between local directory and WooCommerce product.
Set Canvas Size
Specify the dimensions to resize the images.
Filename Template
Define a template for renaming the images using placeholders like {name}, {sku}, {width}, {height}.
Start Processing
Click the "Start Processing" button to process the images. The log will appear in a separate window.
Notes
Ensure ImageMagick is correctly installed and added to your system's PATH.
The application uses the cryptography library to encrypt WooCommerce API credentials for security.
Support
If you encounter any issues or have questions, feel free to open an issue in the repository or contact the maintainer.
License
This project is licensed under the MIT License.
Acknowledgements
ImageMagick
WooCommerce
With this information, you should be able to set up, run, and create an executable for the Image Processing Application. Enjoy processing your images with ease!

204
ui/local_processing_tab.py Normal file
View File

@@ -0,0 +1,204 @@
import tkinter as tk
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText
from tkinter import filedialog
from PIL import Image, ImageTk
import tempfile
import os
from utils.file_operations import browse_directory, process_directory_with_logging, get_first_image_path
from utils.image_processing import set_canvas_size, resize_image
from api.woocommerce import process_product_images, process_all_products
canvas_width = 900
canvas_height = 900
def create_tab_local(tab_parent, text, log):
tab = ttk.Frame(tab_parent)
tab_parent.add(tab, text=text)
log_window = None
def create_log_window():
nonlocal log_window
if log_window and tk.Toplevel.winfo_exists(log_window):
return
log_window = tk.Toplevel()
log_window.title("Processing Log")
log_text = ScrolledText(log_window, state='disabled', wrap='word', height=20, width=80)
log_text.pack(expand=True, fill='both')
def log_message(message):
log_text.config(state='normal')
log_text.insert(tk.END, message + "\n")
log_text.see(tk.END)
log_text.config(state='disabled')
log_text.update_idletasks() # Ensure the GUI updates
return log_message
# Source selection section
source_label = tk.Label(tab, text="Source Type:")
source_label.grid(row=0, column=0, padx=5, pady=5, sticky='w')
source_type = tk.StringVar(value="local")
source_dropdown = ttk.Combobox(tab, textvariable=source_type, values=["local", "product", "all_products"], state="readonly")
source_dropdown.grid(row=1, column=0, padx=5, pady=5, sticky='w')
source_dropdown.bind("<<ComboboxSelected>>", lambda e: update_source_fields())
# Local Directory selection section
def update_directory():
directory = browse_directory()
if directory:
# Show only the last directory name with ../
truncated_directory = f"../{os.path.basename(directory)}"
button_browse.config(text=truncated_directory)
update_previews()
button_browse = tk.Button(tab, text="Browse", command=update_directory)
button_browse.grid(row=2, column=0, padx=5, pady=5, sticky='nw')
# WooCommerce Product ID section
product_id_label = tk.Label(tab, text="Product ID:")
product_id_label.grid(row=2, column=0, padx=5, pady=5, sticky='w')
product_id_entry = tk.Entry(tab)
product_id_entry.grid(row=3, column=0, padx=5, pady=5, sticky='w')
# SKU section
additional_name_label = tk.Label(tab, text="Add suffix:")
additional_name_label.grid(row=3, column=1, padx=5, pady=5, sticky='w')
additional_name_entry = tk.Entry(tab)
additional_name_entry.grid(row=3, column=2, padx=5, pady=5, sticky='w')
# Template section
template_label = tk.Label(tab, text="Filename Template:")
template_label.grid(row=3, column=1, padx=5, pady=5, sticky='w')
template_entry = tk.Entry(tab)
template_entry.insert(0, "{slug}_{sku}_{width}x{height}")
template_entry.grid(row=3, column=2, padx=5, columnspan=2, pady=5, sticky='w')
def update_source_fields():
print(source_type.get())
if source_type.get() == "local":
button_browse.grid()
product_id_label.grid_remove()
product_id_entry.grid_remove()
additional_name_label.grid()
additional_name_entry.grid()
template_entry.grid_remove()
template_label.grid_remove()
elif source_type.get() == "product":
button_browse.grid_remove()
product_id_label.grid()
product_id_entry.grid()
additional_name_label.grid_remove()
additional_name_entry.grid_remove()
else:
button_browse.grid_remove()
product_id_label.grid_remove()
product_id_entry.grid_remove()
product_id_label.grid()
product_id_entry.grid()
additional_name_label.grid_remove()
additional_name_entry.grid_remove()
update_source_fields() # Initial call to set correct fields
# Canvas size section
width_label = tk.Label(tab, text="Canvas Width:")
width_label.grid(row=0, column=1, padx=5, pady=5, sticky='w')
width_entry = tk.Entry(tab)
width_entry.insert(0, "900")
width_entry.grid(row=0, column=2, padx=5, pady=5, sticky='w')
height_label = tk.Label(tab, text="Canvas Height:")
height_label.grid(row=1, column=1, padx=5, pady=5, sticky='w')
height_entry = tk.Entry(tab)
height_entry.insert(0, "900")
height_entry.grid(row=1, column=2, padx=5, pady=5, sticky='w')
def apply_canvas_size():
global canvas_width, canvas_height
canvas_width = int(width_entry.get())
canvas_height = int(height_entry.get())
set_canvas_size(canvas_width, canvas_height)
update_previews()
button_set_size = tk.Button(tab, text="Save Size", command=apply_canvas_size)
button_set_size.grid(row=2, column=1, columnspan=2, padx=5, pady=5, sticky='w')
# Checkbox for deleting images
checkbox_var = tk.BooleanVar()
checkbox = tk.Checkbutton(tab, text="Delete image when done", variable=checkbox_var)
checkbox.grid(row=0, column=3, columnspan=2, padx=5, pady=5, sticky='w')
# Start Processing button
def start_processing():
log_message = None # create_log_window()
if source_type.get() == "local":
process_directory_with_logging(additional_name_entry.get(), checkbox_var.get(), log_message, update_previews)
elif source_type.get() == "product":
product_id = product_id_entry.get()
process_product_images(product_id, template_entry.get(), canvas_width, canvas_height)
else:
process_all_products(template_entry.get(), canvas_width, canvas_height)
button_start = tk.Button(tab, text="Start Processing", command=start_processing)
button_start.grid(row=1, column=3, columnspan=2, padx=5, pady=5, sticky='w')
# Image previews
before_label = tk.Label(tab, text="Before:")
before_label.grid(row=5, column=0, columnspan=2, padx=5, pady=5, sticky='w')
before_image_label = tk.Label(tab)
before_image_label.grid(row=6, columnspan=2, column=0, padx=5, pady=5, sticky='w')
after_label = tk.Label(tab, text="After:")
after_label.grid(row=5, columnspan=2, column=2, padx=5, pady=5, sticky='w')
after_image_label = tk.Label(tab)
after_image_label.grid(row=6, columnspan=2, column=2, padx=5, pady=5, sticky='w')
def update_previews(before_path=None, after_path=None):
if before_path and after_path:
before_img = Image.open(before_path)
before_img.thumbnail((150, 150))
before_photo = ImageTk.PhotoImage(before_img)
before_image_label.config(image=before_photo)
before_image_label.image = before_photo
after_img = Image.open(after_path)
after_img.thumbnail((150, 150))
after_photo = ImageTk.PhotoImage(after_img)
after_image_label.config(image=after_photo)
after_image_label.image = after_photo
else:
first_image_path = get_first_image_path()
if first_image_path:
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as temp_file:
output_path = temp_file.name
resize_image(first_image_path, output_path, additional_name_entry.get())
before_img = Image.open(first_image_path)
before_img.thumbnail((150, 150))
before_photo = ImageTk.PhotoImage(before_img)
before_image_label.config(image=before_photo)
before_image_label.image = before_photo
after_img = Image.open(output_path)
after_img.thumbnail((150, 150))
after_photo = ImageTk.PhotoImage(after_img)
after_image_label.config(image=after_photo)
after_image_label.image = after_photo
# Configure column weights to allow the log_text to expand
tab.columnconfigure(0, weight=1)
tab.columnconfigure(1, weight=1)
tab.columnconfigure(2, weight=1)
tab.columnconfigure(3, weight=1)
tab.rowconfigure(6, weight=1)

16
ui/log_window.py Normal file
View File

@@ -0,0 +1,16 @@
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
class LogWindow(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Processing Log")
self.geometry("600x400")
self.log_text = ScrolledText(self, state='disabled', wrap='word')
self.log_text.pack(expand=True, fill='both')
def log(self, message):
self.log_text.config(state='normal')
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
self.log_text.config(state='disabled')

58
ui/settings_tab.py Normal file
View File

@@ -0,0 +1,58 @@
import tkinter as tk
from tkinter import ttk
from api.woocommerce import save_credentials, load_credentials
def create_tab_settings(tab_parent, text):
tab = ttk.Frame(tab_parent)
tab_parent.add(tab, text=text)
credentials = load_credentials()
url_value = credentials['url'] if credentials else ''
consumer_key_value = credentials['consumer_key'] if credentials else ''
consumer_secret_value = credentials['consumer_secret'] if credentials else ''
username_value = credentials['username'] if credentials else ''
password_value = credentials['password'] if credentials else ''
url_label = tk.Label(tab, text="WooCommerce URL:")
url_label.pack(pady=5)
url_entry = tk.Entry(tab)
url_entry.insert(0, url_value)
url_entry.pack()
consumer_key_label = tk.Label(tab, text="Consumer Key:")
consumer_key_label.pack(pady=5)
consumer_key_entry = tk.Entry(tab)
consumer_key_entry.insert(0, consumer_key_value)
consumer_key_entry.pack()
consumer_secret_label = tk.Label(tab, text="Consumer Secret:")
consumer_secret_label.pack(pady=5)
consumer_secret_entry = tk.Entry(tab, show="*")
consumer_secret_entry.insert(0, consumer_secret_value)
consumer_secret_entry.pack()
username_label = tk.Label(tab, text="Username:")
username_label.pack(pady=5)
username_entry = tk.Entry(tab)
username_entry.insert(0, username_value)
username_entry.pack()
password_label = tk.Label(tab, text="Password:")
password_label.pack(pady=5)
password_entry = tk.Entry(tab, show="*")
password_entry.insert(0, password_value)
password_entry.pack()
button_save = tk.Button(tab, text="Save Credentials", command=lambda: save_credentials(
url_entry.get(),
consumer_key_entry.get(),
consumer_secret_entry.get(),
username_entry.get(),
password_entry.get()
))
button_save.pack(pady=5)

71
utils/file_operations.py Normal file
View File

@@ -0,0 +1,71 @@
import os
import shutil
import tempfile
from tkinter import filedialog, messagebox
from utils.image_processing import resize_image
selected_directory = ""
def browse_directory():
global selected_directory
selected_directory = filedialog.askdirectory()
return selected_directory
def get_first_image_path():
if not selected_directory:
return None
for root, dirs, files in os.walk(selected_directory):
if 'ProcessedImages' in dirs:
dirs.remove('ProcessedImages')
for file in files:
if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp', '.avif')):
return os.path.join(root, file)
return None
def process_directory_with_logging(additional_name, is_checked, log, update_previews):
if not selected_directory:
messagebox.showwarning("No Directory", "Please select a directory.")
return
if log:
log(f"Processing started for directory: {selected_directory}")
output_directory = os.path.join(selected_directory, 'ProcessedImages')
if os.path.exists(output_directory):
shutil.rmtree(output_directory)
if log:
log("Existing directory removed.")
os.makedirs(output_directory, exist_ok=True)
if log:
log(f"Output directory created: {output_directory}")
image_paths = []
for root, dirs, files in os.walk(selected_directory):
if 'ProcessedImages' in dirs:
dirs.remove('ProcessedImages')
for file in files:
if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp', '.avif')):
file_path = os.path.join(root, file)
image_paths.append(file_path)
if log:
log(f"Found: {file_path}")
if log:
log(f"Total images found: {len(image_paths)}")
for file_path in image_paths:
output_path = os.path.join(output_directory, os.path.relpath(file_path, selected_directory))
os.makedirs(os.path.dirname(output_path), exist_ok=True)
resize_image(file_path, output_path, additional_name)
# with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as temp_file:
# temp_output_path = temp_file.name
# resize_image(file_path, temp_output_path, additional_name)
# update_previews(file_path, temp_output_path)
if os.path.exists(file_path) and is_checked:
os.remove(file_path)
if log:
log(f"Processed: {file_path}")
messagebox.showinfo("Process Complete", "Image processing is complete.")
if log:
log("Processing complete.")

28
utils/image_processing.py Normal file
View File

@@ -0,0 +1,28 @@
from wand.image import Image
from wand.color import Color
import os
canvas_width = 900
canvas_height = 900
def set_canvas_size(width, height):
global canvas_width, canvas_height
canvas_width = int(width)
canvas_height = int(height)
def resize_image(image_path, output_path, additional_name):
with Image(filename=image_path) as img:
img.transform(resize=f'{canvas_width}x{canvas_height}>')
x_offset = int((canvas_width - img.width) / 2)
y_offset = int((canvas_height - img.height) / 2)
with Image(width=canvas_width, height=canvas_height, background=Color('transparent')) as canvas:
canvas.composite(img, left=x_offset, top=y_offset)
new_filename = os.path.splitext(os.path.basename(output_path))[0]
if additional_name:
new_filename += " - " + additional_name.strip()
new_filename += os.path.splitext(output_path)[1]
output_path = os.path.join(os.path.dirname(output_path), new_filename)
canvas.save(filename=output_path)