diff --git a/app.py b/app.py index 2cb7fe0..4bc9a9e 100644 --- a/app.py +++ b/app.py @@ -56,7 +56,17 @@ class User(Base): is_admin = Column(Boolean, default=False) # Admin flag -import uuid # Add this import at the top of your file +def get_mime_type(extension): + extension = extension.lower() + if extension == 'jpg' or extension == 'jpeg': + return 'image/jpeg' + elif extension == 'png': + return 'image/png' + elif extension == 'gif': + return 'image/gif' + else: + return 'application/octet-stream' + class Puzzle(Base): __tablename__ = "puzzles" @@ -64,6 +74,7 @@ class Puzzle(Base): date = Column(Date, nullable=False) size = Column(Integer, nullable=False) # 3, 4, or 5 image_b64 = Column(String, nullable=False) + image_extension = Column(String, nullable=False) # Remove the nullable=False constraint as we'll generate these after creation initial_state_json = Column(String) @@ -72,16 +83,25 @@ class Puzzle(Base): title = Column(String, default="Daily Puzzle") is_visible = Column(Boolean, default=True) - def __init__(self, date, size, image_b64, title="Daily Puzzle", is_visible=True): + def __init__(self, date, size, image_b64, image_extension, title="Daily Puzzle", is_visible=True): self.date = date self.size = size self.image_b64 = image_b64 + self.image_extension = image_extension self.title = title self.is_visible = is_visible # Generate initial and solution states self.generate_states() + @property + def filename_slug(self): + return f"{slugify(self.title)}.{self.image_extension}" + + @property + def image_mime_type(self): + return get_mime_type(self.image_extension) + def generate_states(self): total_pieces = self.size * self.size pieces = [] @@ -646,10 +666,10 @@ def upload_post_view(request): # Validate file type allowed_extensions = {"png", "jpg", "jpeg", "gif"} - if not any( - image_file.filename.lower().endswith(f".{ext}") - for ext in allowed_extensions - ): + filename = image_file.filename.lower() + file_extension = os.path.splitext(filename)[1][1:] # Get extension without dot + + if file_extension not in allowed_extensions: return Response( f"Unsupported file type for {size}x{size} puzzle. Allowed types: png, jpg, jpeg, gif.", status=400, @@ -676,6 +696,7 @@ def upload_post_view(request): date=puzzle_date, size=size, image_b64=encoded_str, + image_extension=file_extension, # Pass the file extension here title=title, ) s.add(puzzle) @@ -763,9 +784,6 @@ def daily_puzzle_view(request): attempt.state_json = existing_solve.state_json s.commit() - # Prepare the puzzle filename for the download button - puzzle_filename = slugify(puzzle.title) + ".png" - # Get user's rank if they have solved it user_rank = None if existing_solve: @@ -786,8 +804,6 @@ def daily_puzzle_view(request): user_rank = idx + 1 break - - return { "request": request, "puzzle": puzzle, @@ -797,6 +813,7 @@ def daily_puzzle_view(request): "rotation_count": attempt.rotation_count, "attempt_is_solved": attempt.is_solved or existing_solve is not None, "user_rank": user_rank, + "image_mime_type": puzzle.image_mime_type, } diff --git a/templates/daily_puzzle.html.j2 b/templates/daily_puzzle.html.j2 index 0caf906..1178ecc 100644 --- a/templates/daily_puzzle.html.j2 +++ b/templates/daily_puzzle.html.j2 @@ -364,7 +364,7 @@ document.addEventListener("DOMContentLoaded", function() { function downloadImage() { const link = document.createElement('a'); link.href = 'data:image/png;base64,{{ puzzle.image_b64 }}'; - link.download = "{{ puzzle.filename }}"; + link.download = "{{ puzzle.filename_slug }}"; document.body.appendChild(link); link.click(); document.body.removeChild(link);