from flask import Flask, jsonify, request, send_from_directory, render_template import os from PIL import Image, ExifTags from apscheduler.schedulers.background import BackgroundScheduler from datetime import datetime, timedelta import piexif import io import random from colorthief import ColorThief import colorsys app = Flask(__name__) IMAGE_FOLDER = '/home/dubey/projects/photoportfolio/pythonserver/images/' THUMBS_FOLDER = '/home/dubey/projects/photoportfolio/pythonserver/thumbs/' IMAGES_PER_PAGE = 5 THUMBNAIL_SIZES = [256, 512, 768, 1024, 1536, 2048] scheduler = BackgroundScheduler() scheduler.start() def generate_thumbnails(): for filename in os.listdir(IMAGE_FOLDER): if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')): original_path = os.path.join(IMAGE_FOLDER, filename) for size in THUMBNAIL_SIZES: thumb_path = os.path.join(THUMBS_FOLDER, f"{size}_{filename}") if not os.path.exists(thumb_path): with Image.open(original_path) as img: # Extract EXIF data exif_data = None if "exif" in img.info: exif_data = img.info["exif"] # Resize image img.thumbnail((size, size), Image.LANCZOS) # Save image with EXIF data if exif_data: img.save(thumb_path, exif=exif_data, optimize=True, quality=85) else: img.save(thumb_path, optimize=True, quality=85) scheduler.add_job(generate_thumbnails, 'interval', minutes=5) scheduler.add_job(generate_thumbnails, 'date', run_date=datetime.now() + timedelta(seconds=1)) # Run once at startup def get_highlight_color(image_path): color_thief = ColorThief(image_path) palette = color_thief.get_palette(color_count=6, quality=1) # Convert RGB to HSV and find the color with the highest saturation highlight_color = max(palette, key=lambda rgb: colorsys.rgb_to_hsv(*rgb)[1]) return '#{:02x}{:02x}{:02x}'.format(*highlight_color) def get_image_info(filename): path = os.path.join(IMAGE_FOLDER, filename) thumb_path = os.path.join(THUMBS_FOLDER, f"256_{filename}") exif = None with Image.open(path) as img: width, height = img.size exif = { ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS } if str(exif['Orientation']) == "6" or str(exif['Orientation']) == "8": width, height = height, width exposure_time = exif['ExposureTime'] if isinstance(exposure_time, tuple): exposure_fraction = f"{exposure_time[0]}/{exposure_time[1]}" else: exposure_fraction = f"1/{int(1/float(exposure_time))}" date_obj = datetime.strptime(exif['DateTime'], '%Y:%m:%d %H:%M:%S') date = date_obj.strftime('%y %m %d') # Format: YY MM DD technical_info = f"{exif['FocalLengthIn35mmFilm']}MM | f/{exif['FNumber']} | {exposure_fraction}S | ISO{exif['ISOSpeedRatings']}" factor = random.randint(2, 3) if height < 4000 or width < 4000: factor = 1 highlight_color = get_highlight_color(thumb_path) return { 'imgSrc': f'/thumbs/1536_{filename}', 'fullSizeImgSrc': f'/images/{filename}', 'date': date, 'technicalInfo': technical_info, 'width': width/factor, 'height': height/factor, 'highlightColor': highlight_color } def get_image_taken_date(filename): path = os.path.join(IMAGE_FOLDER, filename) with Image.open(path) as img: exif = { ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS } date_str = exif.get('DateTime', exif.get('DateTimeOriginal', '')) if date_str: return datetime.strptime(date_str, '%Y:%m:%d %H:%M:%S') return datetime.fromtimestamp(os.path.getmtime(path)) # Fallback to file modification time @app.route('/api/images') def get_images(): page = int(request.args.get('page', 1)) all_images = sorted( [f for f in os.listdir(IMAGE_FOLDER) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif'))], key=get_image_taken_date, reverse=True ) start = (page - 1) * IMAGES_PER_PAGE end = start + IMAGES_PER_PAGE page_images = all_images[start:end] return jsonify({ 'images': [get_image_info(img) for img in page_images], 'hasMore': end < len(all_images) }) @app.route('/images/') def serve_image(filename): return send_from_directory(IMAGE_FOLDER, filename) @app.route('/') def index(): return render_template('index.html') @app.route('/thumbs/') def serve_thumbnail(filename): return send_from_directory(THUMBS_FOLDER, filename) if __name__ == '__main__': app.run(debug=True, port=5001, host='0.0.0.0')