diff --git a/app.py b/app.py index df234a1..1984c71 100644 --- a/app.py +++ b/app.py @@ -24,8 +24,8 @@ from wtforms.validators import DataRequired, Length, EqualTo, ValidationError, R from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired, EqualTo, Regexp from flask_wtf import FlaskForm, RecaptchaField -from dotenv import load_dotenv from dotenv import load_dotenv, find_dotenv +import aiofiles.os dotenv_path = find_dotenv() load_dotenv(dotenv_path, override=True) @@ -47,7 +47,7 @@ app.config.update( 'avatars': 'static/avatars/', 'banners': 'static/banners/', 'comics': 'static/comics', - 'comicthumbs': 'static/comicthumbs', + 'comicthumbs': 'static/comicthumbs/', 'posts': 'static/posts/' }, ALLOWED_IMAGE_EXTENSIONS={'png', 'jpg', 'jpeg', 'gif', 'webp'}, @@ -557,88 +557,66 @@ class UploadForm(FlaskForm): validators=[DataRequired(message="You must agree with the publication rules.")]) submit = SubmitField('Upload') -def convert_to_webp(image_file): +async def convert_to_webp(image_file): + loop = asyncio.get_event_loop() + return await loop.run_in_executor(None, _sync_convert_to_webp, image_file) - img = PILImage.open(image_file) - output = io.BytesIO() - - img.convert("RGB").save(output, format="WEBP", quality=90, optimize=True) - output.seek(0) - return output +def _sync_convert_to_webp(image_file): + with PILImage.open(image_file) as img: + output = io.BytesIO() + img.convert("RGB").save(output, format="WEBP", quality=90, optimize=True) + output.seek(0) + return output @app.route('/upload', methods=['GET', 'POST']) @login_required -def upload(): +async def upload(): form = UploadForm() - if form.validate_on_submit(): image_file = form.image_file.data tags = form.tags.data - - if image_file: - - if not allowed_file(image_file.filename, app.config['ALLOWED_IMAGE_EXTENSIONS']): - return redirect(url_for('upload')) - - if not check_file_size(image_file, app.config['MAX_IMAGE_SIZE']): - return redirect(url_for('upload')) - - unique_filename = f"{uuid.uuid4().hex}.webp" - filepath = os.path.join(app.config['UPLOAD_FOLDER']['images'], unique_filename) - - if os.path.exists(filepath): - return redirect(url_for('upload')) - - webp_image = convert_to_webp(image_file) - - try: - with open(filepath, 'wb') as f: - f.write(webp_image.read()) - except Exception as e: - return redirect(url_for('upload')) - - img = Image(image_file=unique_filename, username=current_user.username, tags=tags, cookie_votes=0) - db.session.add(img) - - user_cookie = Cookies.query.filter_by(username=current_user.username).first() - if user_cookie: - user_cookie.cookies += 1 - else: - user_cookie = Cookies(username=current_user.username, cookies=1) - db.session.add(user_cookie) - - try: - db.session.commit() - except Exception as e: - db.session.rollback() - return redirect(url_for('upload')) - - return redirect(url_for('index')) - else: + if not (allowed_file(image_file.filename, app.config['ALLOWED_IMAGE_EXTENSIONS']) and + await check_file_size(image_file, app.config['MAX_IMAGE_SIZE'])): return redirect(url_for('upload')) - + unique_filename = f"{uuid.uuid4().hex}.webp" + filepath = os.path.join(app.config['UPLOAD_FOLDER']['images'], unique_filename) + if os.path.exists(filepath): + return redirect(url_for('upload')) + webp_image = await convert_to_webp(image_file) + async with aiofiles.open(filepath, 'wb') as f: + await f.write(webp_image.read()) + img = Image(image_file=unique_filename, username=current_user.username, tags=tags, cookie_votes=0) + db.session.add(img) + user_cookie = Cookies.query.filter_by(username=current_user.username).first() + if user_cookie: + user_cookie.cookies += 1 + else: + user_cookie = Cookies(username=current_user.username, cookies=1) + db.session.add(user_cookie) + db.session.commit() + return redirect(url_for('index')) return render_template('upload.html', form=form) def allowed_file(filename, allowed_extensions): return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions -def check_file_size(file, max_size): +async def check_file_size(file, max_size): + loop = asyncio.get_event_loop() + return await loop.run_in_executor(None, _sync_check_file_size, file, max_size) + +def _sync_check_file_size(file, max_size): file.seek(0, os.SEEK_END) file_size = file.tell() file.seek(0) return file_size <= max_size -def generate_unique_filename(filename, upload_folder): - +async def generate_unique_filename(filename, upload_folder): base, ext = os.path.splitext(secure_filename(filename)) - unique_filename = f"{base}_{uuid.uuid4().hex}{ext}" - file_path = os.path.join(upload_folder, unique_filename) - - while os.path.exists(file_path): + while True: unique_filename = f"{base}_{uuid.uuid4().hex}{ext}" file_path = os.path.join(upload_folder, unique_filename) - - return unique_filename + if not await aiofiles.os.path.exists(file_path): + return unique_filename class UploadVideoForm(FlaskForm): video_file = FileField('Video File', validators=[DataRequired()]) @@ -1595,7 +1573,6 @@ def buy_item(item_id): db.session.commit() return redirect(url_for('shop')) - if __name__ == '__main__': with app.app_context(): db.create_all() diff --git a/templates/view.html b/templates/view.html index e61755e..5695cf2 100644 --- a/templates/view.html +++ b/templates/view.html @@ -133,8 +133,7 @@

Votes: {{ content.cookie_votes }} 🍪

{% if current_user.is_authenticated %} -
+