EXIF-METADATA-ADDER / src /streamlit_app.py
userIdc2024's picture
Update src/streamlit_app.py
258fa90 verified
import streamlit as st
import zipfile
import io
import os
import logging
from dotenv import load_dotenv
from meta_data import meta_data_helper_function
# Load environment variables from .env file
load_dotenv()
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Set page config to full screen
st.set_page_config(
page_title="EXIF Metadata Adder",
page_icon="πŸ“·",
layout="wide"
)
def check_token(user_token):
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
if not ACCESS_TOKEN:
logger.critical("ACCESS_TOKEN not set in environment.")
return False, "Server error: Access token not configured."
if user_token == ACCESS_TOKEN:
logger.info("Access token validated successfully.")
return True, ""
logger.warning("Invalid access token attempt.")
return False, "Invalid token."
def is_image_file(filename):
"""Check if file is a supported image format."""
return filename.lower().endswith(('.jpeg', '.jpg', '.png', '.webp'))
def process_single_image(image_bytes):
"""Process a single image and add EXIF metadata."""
try:
return meta_data_helper_function(image_bytes)
except Exception as e:
st.error(f"Error processing image: {str(e)}")
return None
def extract_images_from_zip(zip_file):
"""Extract image files from uploaded ZIP."""
images = {}
try:
with zipfile.ZipFile(zip_file, 'r') as zip_ref:
for file_info in zip_ref.filelist:
if not file_info.is_dir() and is_image_file(file_info.filename):
# Read the image data
image_data = zip_ref.read(file_info.filename)
# Use just the filename, not the full path
clean_filename = os.path.basename(file_info.filename)
images[clean_filename] = image_data
except Exception as e:
st.error(f"Error extracting ZIP file: {str(e)}")
return images
def create_download_zip(processed_images):
"""Create a ZIP file containing all processed images."""
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for filename, image_data in processed_images.items():
# Ensure filename has .jpg extension
if not filename.lower().endswith('.jpg'):
name, _ = os.path.splitext(filename)
filename = f"{name}.jpg"
zip_file.writestr(filename, image_data)
zip_buffer.seek(0)
return zip_buffer.getvalue()
def main():
st.title("πŸ“· EXIF Metadata Adder")
st.write("Upload images or ZIP files to add realistic synthetic EXIF metadata")
# Authentication section
if 'authenticated' not in st.session_state:
st.session_state.authenticated = False
if not st.session_state.authenticated:
st.header("πŸ” Authentication Required")
st.write("Please enter your access token to continue:")
token_input = st.text_input(
"Access Token",
type="password",
help="Enter your access token to access the EXIF metadata tools"
)
col1, col2 = st.columns([1, 3])
with col1:
if st.button("πŸ”“ Login", type="primary"):
if token_input:
is_valid, error_msg = check_token(token_input)
if is_valid:
st.session_state.authenticated = True
st.success("βœ… Authentication successful!")
st.rerun()
else:
st.error(f"❌ {error_msg}")
else:
st.warning("⚠️ Please enter an access token")
return
# Show logout option in sidebar when authenticated
if st.session_state.authenticated:
if st.button("πŸ”’ Logout"):
st.session_state.authenticated = False
st.rerun()
# Main app functionality (only shown when authenticated)
# File uploader
uploaded_files = st.file_uploader(
"Choose images or ZIP files",
type=['jpeg', 'jpg', 'png', 'webp', 'zip'],
accept_multiple_files=True,
help="Supported formats: JPEG, JPG, PNG, WEBP, ZIP"
)
if uploaded_files:
# Separate images and ZIP files
image_files = []
zip_files = []
for file in uploaded_files:
if file.name.lower().endswith('.zip'):
zip_files.append(file)
elif is_image_file(file.name):
image_files.append(file)
# Process files
all_images_to_process = {}
# Add individual image files
for img_file in image_files:
all_images_to_process[img_file.name] = img_file.read()
# Extract images from ZIP files
for zip_file in zip_files:
st.info(f"πŸ“¦ Extracting images from {zip_file.name}...")
extracted_images = extract_images_from_zip(zip_file)
all_images_to_process.update(extracted_images)
if all_images_to_process:
st.success(f"βœ… Found {len(all_images_to_process)} image(s) to process")
# Show file list
with st.expander("πŸ“‹ Files to Process"):
for filename in all_images_to_process.keys():
st.write(f"β€’ {filename}")
# Process button
if st.button("πŸ”„ Add EXIF Metadata", type="primary"):
progress_bar = st.progress(0)
status_text = st.empty()
processed_images = {}
total_files = len(all_images_to_process)
for i, (filename, image_data) in enumerate(all_images_to_process.items()):
status_text.text(f"Processing {filename}...")
# Process the image
processed_data = process_single_image(image_data)
if processed_data:
# Ensure output filename has .jpg extension
name, _ = os.path.splitext(filename)
output_filename = f"{name}.jpg"
processed_images[output_filename] = processed_data
# Update progress
progress_bar.progress((i + 1) / total_files)
status_text.text("βœ… Processing complete!")
if processed_images:
st.success(f"πŸŽ‰ Successfully processed {len(processed_images)} image(s)")
# Download options
if len(processed_images) == 1:
# Single image download
filename, image_data = next(iter(processed_images.items()))
st.download_button(
label=f"πŸ“₯ Download {filename}",
data=image_data,
file_name=filename,
mime="image/jpeg",
type="primary"
)
else:
# Multiple images - create ZIP
zip_data = create_download_zip(processed_images)
st.download_button(
label=f"πŸ“₯ Download All Images ({len(processed_images)} files)",
data=zip_data,
file_name="processed_images_with_exif.zip",
mime="application/zip",
type="primary"
)
else:
st.error("❌ No images could be processed successfully")
else:
st.warning("⚠️ No valid image files found in uploaded files")
if __name__ == "__main__":
main()