146 lines
4.9 KiB
Python
146 lines
4.9 KiB
Python
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} | 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/<path:filename>')
|
|
def serve_image(filename):
|
|
return send_from_directory(IMAGE_FOLDER, filename)
|
|
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('index.html')
|
|
|
|
|
|
@app.route('/thumbs/<path:filename>')
|
|
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')
|