973 lines
36 KiB
Python
973 lines
36 KiB
Python
import os
|
|
import io
|
|
import re
|
|
import uuid
|
|
import shutil
|
|
import random
|
|
import asyncio
|
|
from datetime import datetime
|
|
import requests
|
|
from authlib.integrations.flask_client import OAuth
|
|
from auth import auth_bp, init_oauth
|
|
from config import Config, configure_oauth
|
|
from flask_session import Session
|
|
from PIL import Image as PILImage
|
|
from sqlalchemy.exc import IntegrityError
|
|
from werkzeug.exceptions import BadRequest
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
from werkzeug.utils import secure_filename
|
|
from flask import Flask, abort, render_template, redirect, url_for, request, session, jsonify
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
from flask_bcrypt import Bcrypt
|
|
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
|
from flask_wtf import FlaskForm, RecaptchaField, CSRFProtect
|
|
from flask_wtf.file import FileAllowed
|
|
from flask_wtf.csrf import validate_csrf
|
|
from wtforms import StringField, PasswordField, SubmitField, FileField, BooleanField, RadioField, SelectField, TextAreaField
|
|
from wtforms.validators import DataRequired, Length, EqualTo, ValidationError, Regexp
|
|
from dotenv import load_dotenv, find_dotenv
|
|
import aiofiles.os
|
|
from sqlalchemy import func, or_
|
|
import magic
|
|
from config import Config
|
|
from models import db, bcrypt, User, Comments, Image, Votes, VideoVotes, Video, Comic, ComicPage, ComicVotes, Cookies, Views, Item, UserItem, Post, Subscription, EditTagsForm, EditVideoForm, RegistrationForm, LoginForm, EmptyForm
|
|
from admin import register_admin_routes
|
|
from upload import upload_bp
|
|
from utils import allowed_file, check_file_content, check_file_size, convert_to_webp, generate_unique_filename, get_content_query, get_client_ip, get_autocomplete_suggestions
|
|
from auth import auth_bp
|
|
|
|
app = Flask(__name__)
|
|
csrf = CSRFProtect(app)
|
|
app.config.from_object(Config)
|
|
|
|
oauth = configure_oauth(app)
|
|
|
|
init_oauth(oauth)
|
|
|
|
db.init_app(app)
|
|
bcrypt.init_app(app)
|
|
login_manager = LoginManager(app)
|
|
login_manager.login_view = 'auth.login'
|
|
|
|
@login_manager.user_loader
|
|
def load_user(user_id):
|
|
return db.session.get(User, int(user_id))
|
|
|
|
from flask_session import Session
|
|
|
|
app.config['SESSION_TYPE'] = 'filesystem'
|
|
app.config['SESSION_FILE_DIR'] = './flask_session'
|
|
app.config['SESSION_PERMANENT'] = False
|
|
app.config['SESSION_USE_SIGNER'] = True
|
|
app.config['SECRET_KEY'] = Config.SECRET_KEY
|
|
Session(app)
|
|
|
|
register_admin_routes(app)
|
|
app.register_blueprint(upload_bp)
|
|
app.register_blueprint(auth_bp)
|
|
|
|
@app.route('/')
|
|
def index():
|
|
page = request.args.get('page', 1, type=int)
|
|
search_query = request.args.get('search')
|
|
|
|
subscriptions = []
|
|
if current_user.is_authenticated:
|
|
|
|
subscriptions = [
|
|
sub.author.username
|
|
for sub in Subscription.query.filter_by(user_id=current_user.id).all()
|
|
]
|
|
|
|
query = get_content_query(Image, subscriptions, search_query)
|
|
pagination = query.paginate(page=page, per_page=25, error_out=False)
|
|
|
|
user_cookies = 0
|
|
if current_user.is_authenticated:
|
|
user_cookies_record = Cookies.query.filter_by(username=current_user.username).first()
|
|
user_cookies = user_cookies_record.cookies if user_cookies_record else 0
|
|
|
|
return render_template(
|
|
'index.html',
|
|
images=pagination.items,
|
|
pagination=pagination,
|
|
user_cookies=user_cookies,
|
|
search_query=search_query,
|
|
content_type='art'
|
|
)
|
|
|
|
@app.route('/arts')
|
|
def arts():
|
|
return render_template(
|
|
'arts.html'
|
|
)
|
|
|
|
@app.route('/catalogue/arts')
|
|
def arts_catalogue():
|
|
return render_template(
|
|
'arts-catalogue.html'
|
|
)
|
|
|
|
@app.route('/vote_art/<int:image_id>', methods=['POST'])
|
|
@login_required
|
|
def vote_art(image_id):
|
|
image = Image.query.get_or_404(image_id)
|
|
user_cookies = Cookies.query.filter_by(username=current_user.username).first()
|
|
|
|
if image.username == current_user.username:
|
|
return redirect(url_for('view', content_type='art', id=image_id))
|
|
|
|
if user_cookies and user_cookies.cookies > 0:
|
|
existing_vote = Votes.query.filter_by(username=current_user.username, image_id=image_id).first()
|
|
if not existing_vote:
|
|
user_cookies.cookies -= 1
|
|
image.cookie_votes += 1
|
|
|
|
new_vote = Votes(username=current_user.username, image_id=image.id)
|
|
db.session.add(new_vote)
|
|
|
|
db.session.commit()
|
|
|
|
return redirect(url_for('view', content_type='art', id=image_id))
|
|
|
|
@app.route('/view/<content_type>/<int:id>', methods=['GET', 'POST'])
|
|
def view(content_type, id):
|
|
comments = []
|
|
avatars = {user.username: user.avatar_file for user in User.query.all()}
|
|
cu = current_user.username if current_user.is_authenticated else None
|
|
|
|
user_cookies = 0
|
|
if current_user.is_authenticated:
|
|
user_cookies_record = Cookies.query.filter_by(username=current_user.username).first()
|
|
user_cookies = user_cookies_record.cookies if user_cookies_record else 0
|
|
|
|
if content_type == 'art':
|
|
content = Image.query.get_or_404(id)
|
|
comments = Comments.query.filter_by(image_id=id).order_by(Comments.comment_date.desc()).all()
|
|
|
|
if current_user.is_authenticated:
|
|
existing_view = Views.query.filter_by(image_id=id, username=current_user.username).first()
|
|
if not existing_view:
|
|
new_view = Views(image_id=id, username=current_user.username)
|
|
db.session.add(new_view)
|
|
db.session.commit()
|
|
|
|
search_query = request.args.get('search')
|
|
page = request.args.get('page', 1, type=int)
|
|
subscriptions = []
|
|
|
|
if current_user.is_authenticated:
|
|
subscriptions = [
|
|
sub.author.username
|
|
for sub in Subscription.query.filter_by(user_id=current_user.id).all()
|
|
]
|
|
|
|
query = get_content_query(Image, subscriptions, search_query)
|
|
all_images = query.all()
|
|
image_ids = [image.id for image in all_images]
|
|
|
|
current_index = image_ids.index(id)
|
|
prev_index = (current_index - 1) % len(image_ids)
|
|
next_index = (current_index + 1) % len(image_ids)
|
|
|
|
prev_content = all_images[prev_index]
|
|
next_content = all_images[next_index]
|
|
random_content = random.choice(all_images)
|
|
|
|
elif content_type == 'video':
|
|
content = Video.query.get_or_404(id)
|
|
comments = Comments.query.filter_by(video_id=id).order_by(Comments.comment_date.desc()).all()
|
|
|
|
if current_user.is_authenticated:
|
|
existing_view = Views.query.filter_by(video_id=id, username=current_user.username).first()
|
|
if not existing_view:
|
|
new_view = Views(video_id=id, username=current_user.username)
|
|
db.session.add(new_view)
|
|
db.session.commit()
|
|
|
|
all_videos = Video.query.order_by(Video.id).all()
|
|
video_ids = [video.id for video in all_videos]
|
|
current_index = video_ids.index(id)
|
|
prev_index = (current_index - 1) % len(all_videos)
|
|
next_index = (current_index + 1) % len(all_videos)
|
|
|
|
prev_content = all_videos[prev_index]
|
|
next_content = all_videos[next_index]
|
|
|
|
random_content = random.choice(all_videos)
|
|
|
|
elif content_type == 'comic':
|
|
content = Comic.query.get_or_404(id)
|
|
comments = Comments.query.filter_by(comic_id=id).order_by(Comments.comment_date.desc()).all()
|
|
|
|
if current_user.is_authenticated:
|
|
existing_view = Views.query.filter_by(image_id=id, username=current_user.username).first()
|
|
if not existing_view:
|
|
new_view = Views(image_id=id, username=current_user.username)
|
|
db.session.add(new_view)
|
|
db.session.commit()
|
|
|
|
comic_pages = ComicPage.query.filter_by(comic_id=id).order_by(ComicPage.page_number).all()
|
|
|
|
if not comic_pages:
|
|
return render_template('error.html', message="Comic pages not found")
|
|
|
|
all_comics = Comic.query.order_by(Comic.id).all()
|
|
comic_ids = [comic.id for comic in all_comics]
|
|
current_index = comic_ids.index(id)
|
|
prev_index = (current_index - 1) % len(all_comics)
|
|
next_index = (current_index + 1) % len(all_comics)
|
|
|
|
prev_content = all_comics[prev_index]
|
|
next_content = all_comics[next_index]
|
|
|
|
random_content = random.choice(all_comics)
|
|
|
|
else:
|
|
abort(404)
|
|
|
|
if request.method == 'POST' and current_user.is_authenticated:
|
|
comment_text = request.form.get('comment')
|
|
max_comment_length = 44
|
|
if comment_text and len(comment_text) > max_comment_length:
|
|
return redirect(url_for('view', content_type=content_type, id=id))
|
|
|
|
if comment_text:
|
|
new_comment = None
|
|
if content_type == 'art':
|
|
new_comment = Comments(username=cu, image_id=id, comment_text=comment_text)
|
|
elif content_type == 'video':
|
|
new_comment = Comments(username=cu, video_id=id, comment_text=comment_text)
|
|
elif content_type == 'comic':
|
|
new_comment = Comments(username=cu, comic_id=id, comment_text=comment_text)
|
|
|
|
if new_comment:
|
|
db.session.add(new_comment)
|
|
db.session.commit()
|
|
return redirect(url_for('view', content_type=content_type, id=id))
|
|
|
|
return render_template(
|
|
'view.html',
|
|
content=content,
|
|
content_type=content_type,
|
|
comments=comments,
|
|
avatars=avatars,
|
|
prev_content=prev_content,
|
|
next_content=next_content,
|
|
random_content=random_content,
|
|
user_cookies=user_cookies,
|
|
comic_pages=comic_pages if content_type == 'comic' else None
|
|
)
|
|
|
|
@app.route('/images')
|
|
def drawings():
|
|
return render_template(
|
|
'images.html'
|
|
)
|
|
|
|
@app.route('/tags_list/<page_type>')
|
|
def tags_list(page_type):
|
|
comics_tags = [comic.tags for comic in Comic.query.all() if comic.tags]
|
|
images_tags = [image.tags for image in Image.query.all() if image.tags]
|
|
videos_tags = [video.tags for video in Video.query.all() if video.tags]
|
|
|
|
if page_type == 'video':
|
|
all_tags = [tag.strip() for tags in videos_tags for tag in tags.split(',')]
|
|
elif page_type == 'comic':
|
|
all_tags = [tag.strip() for tags in comics_tags for tag in tags.split(',')]
|
|
elif page_type == 'image':
|
|
all_tags = [tag.strip() for tags in images_tags for tag in tags.split(',')]
|
|
else:
|
|
all_tags = set(tag.strip() for tags in (comics_tags + images_tags + videos_tags) for tag in tags.split(','))
|
|
|
|
sorted_tags = sorted(set(all_tags))
|
|
|
|
return render_template('tags_list.html', tags=sorted_tags, page_type=page_type)
|
|
|
|
@app.route('/subnav')
|
|
def subnav():
|
|
return render_template(
|
|
'subnav.html'
|
|
)
|
|
|
|
@app.route('/image_edit/<int:id>', methods=['GET', 'POST'])
|
|
@login_required
|
|
def image_edit(id):
|
|
|
|
image = Image.query.get_or_404(id)
|
|
|
|
if image.username != current_user.username:
|
|
return redirect(url_for('index'))
|
|
|
|
form = EditTagsForm()
|
|
|
|
if form.validate_on_submit():
|
|
|
|
image.tags = form.tags.data
|
|
db.session.commit()
|
|
return redirect(url_for('view', content_type='art', id=id))
|
|
|
|
if request.method == 'GET':
|
|
form.tags.data = image.tags
|
|
|
|
image_preview_url = url_for('static', filename=f'arts/{image.image_file}')
|
|
|
|
return render_template(
|
|
'image_edit.html',
|
|
form=form,
|
|
image=image,
|
|
image_preview_url=image_preview_url
|
|
)
|
|
|
|
@app.route('/video_edit/<int:id>', methods=['GET', 'POST'])
|
|
@login_required
|
|
async def video_edit(id):
|
|
video = Video.query.get_or_404(id)
|
|
|
|
if video.username != current_user.username:
|
|
return redirect(url_for('index'))
|
|
|
|
form = EditVideoForm()
|
|
|
|
if request.method == 'GET':
|
|
form.video_name.data = video.video_name
|
|
form.description.data = video.description
|
|
form.tags.data = video.tags
|
|
|
|
if form.validate_on_submit():
|
|
video.video_name = form.video_name.data
|
|
video.description = form.description.data
|
|
video.tags = form.tags.data
|
|
|
|
if form.video_thumbnail.data:
|
|
thumbnail_file = form.video_thumbnail.data
|
|
if allowed_file(thumbnail_file.filename, app.config['ALLOWED_IMAGE_EXTENSIONS']):
|
|
|
|
thumbnail_filename = await generate_unique_filename(
|
|
app.config['UPLOAD_FOLDER']['thumbnails'], 'webp'
|
|
)
|
|
thumbnail_path = os.path.join(app.config['UPLOAD_FOLDER']['thumbnails'], thumbnail_filename)
|
|
|
|
webp_thumbnail = await convert_to_webp(thumbnail_file)
|
|
async with aiofiles.open(thumbnail_path, 'wb') as f:
|
|
await f.write(webp_thumbnail.read())
|
|
|
|
video.video_thumbnail_file = thumbnail_filename
|
|
|
|
db.session.commit()
|
|
return redirect(url_for('view', content_type='video', id=id))
|
|
|
|
video_preview_url = url_for('static', filename=f'videos/{video.video_thumbnail_file}')
|
|
|
|
return render_template(
|
|
'video_edit.html',
|
|
form=form,
|
|
video=video,
|
|
video_preview_url=video_preview_url
|
|
)
|
|
|
|
@app.route('/navbar')
|
|
def navbar():
|
|
return render_template(
|
|
'navbar.html'
|
|
)
|
|
|
|
@app.route('/card')
|
|
def card():
|
|
return render_template(
|
|
'card.html'
|
|
)
|
|
|
|
@app.route('/gifs')
|
|
def gifs():
|
|
return render_template(
|
|
'gifs.html'
|
|
)
|
|
|
|
@app.route('/autocomplete')
|
|
def autocomplete():
|
|
search_query = request.args.get('search', '', type=str)
|
|
|
|
if search_query:
|
|
|
|
suggestions = get_autocomplete_suggestions(search_query)
|
|
else:
|
|
suggestions = []
|
|
|
|
return jsonify(suggestions)
|
|
|
|
@app.route('/user_pubs/<pub_type>/<username>')
|
|
def user_pubs(pub_type, username):
|
|
p = request.args.get('page', 1, type=int)
|
|
search_query = request.args.get('search', '')
|
|
|
|
if pub_type == 'arts':
|
|
query = Image.query.filter_by(username=username)
|
|
if search_query:
|
|
|
|
query = query.filter(or_(
|
|
Image.image_file.ilike(f'%{search_query}%'),
|
|
Image.tags.ilike(f'%{search_query}%')
|
|
))
|
|
items = query.order_by(Image.publication_date.desc()).paginate(page=p, per_page=25, error_out=False)
|
|
|
|
elif pub_type == 'videos':
|
|
query = Video.query.filter_by(username=username)
|
|
if search_query:
|
|
|
|
query = query.filter(Video.video_name.ilike(f'%{search_query}%'))
|
|
items = query.order_by(Video.publication_date.desc()).paginate(page=p, per_page=25, error_out=False)
|
|
|
|
elif pub_type == 'comics':
|
|
query = Comic.query.filter_by(username=username)
|
|
if search_query:
|
|
|
|
query = query.filter(Comic.name.ilike(f'%{search_query}%'))
|
|
items = query.order_by(Comic.publication_date.desc()).paginate(page=p, per_page=25, error_out=False)
|
|
|
|
else:
|
|
abort(404)
|
|
|
|
user_cookies = Cookies.query.filter_by(username=username).first()
|
|
cookies_count = user_cookies.cookies if user_cookies else 0
|
|
|
|
return render_template(
|
|
'user_pubs.html',
|
|
items=items.items,
|
|
pagination=items,
|
|
pub_type=pub_type,
|
|
username=username,
|
|
cookies_count=cookies_count,
|
|
search_query=search_query
|
|
)
|
|
|
|
@app.route('/delete/<content_type>/<int:content_id>', methods=['POST'])
|
|
@login_required
|
|
def delete(content_type, content_id):
|
|
if content_type == 'art':
|
|
content = Image.query.get_or_404(content_id)
|
|
file_path = os.path.join(app.config['UPLOAD_FOLDER']['arts'], content.image_file)
|
|
elif content_type == 'video':
|
|
content = Video.query.get_or_404(content_id)
|
|
file_path = os.path.join(app.config['UPLOAD_FOLDER']['videos'], content.video_file)
|
|
elif content_type == 'comic':
|
|
content = Comic.query.get_or_404(content_id)
|
|
file_path = os.path.join(app.config['UPLOAD_FOLDER']['comics'], content.comic_folder)
|
|
else:
|
|
abort(404, "Invalid content type")
|
|
|
|
if content.username != current_user.username:
|
|
abort(403)
|
|
|
|
if os.path.exists(file_path):
|
|
if os.path.isdir(file_path):
|
|
shutil.rmtree(file_path)
|
|
else:
|
|
os.remove(file_path)
|
|
|
|
db.session.delete(content)
|
|
db.session.commit()
|
|
return redirect(url_for('profile', username=current_user.username))
|
|
|
|
|
|
@app.route('/delete_comment/<int:comment_id>', methods=['POST'])
|
|
@login_required
|
|
def delete_comment(comment_id):
|
|
comment = db.session.get(Comments, comment_id) or abort(404)
|
|
|
|
if comment.image_id:
|
|
content_type = 'art'
|
|
content_id = comment.image_id
|
|
elif comment.video_id:
|
|
content_type = 'video'
|
|
content_id = comment.video_id
|
|
else:
|
|
content_type = 'comic'
|
|
content_id = comment.comic_id
|
|
|
|
if comment.username == current_user.username:
|
|
try:
|
|
if hasattr(comment, 'post_id') and comment.post_id:
|
|
post_id = comment.post_id
|
|
db.session.delete(comment)
|
|
db.session.commit()
|
|
return redirect(url_for('user_posts', username=current_user.username))
|
|
|
|
db.session.delete(comment)
|
|
db.session.commit()
|
|
return redirect(url_for('view', content_type=content_type, id=content_id))
|
|
except IntegrityError:
|
|
db.session.rollback()
|
|
return redirect(url_for('view', content_type=content_type, id=content_id))
|
|
|
|
return redirect(request.referrer or url_for('index'))
|
|
|
|
@app.route('/videos')
|
|
def videos():
|
|
page = request.args.get('page', 1, type=int)
|
|
search_query = request.args.get('search')
|
|
|
|
subscriptions = []
|
|
if current_user.is_authenticated:
|
|
subscriptions = [sub.author_id for sub in Subscription.query.filter_by(user_id=current_user.id).all()]
|
|
|
|
query = get_content_query(Video, subscriptions, search_query)
|
|
pagination = query.paginate(page=page, per_page=10, error_out=False)
|
|
|
|
videos_with_views = []
|
|
for video in pagination.items:
|
|
views_count = db.session.query(func.count(Views.id)).filter(Views.video_id == video.id).scalar()
|
|
videos_with_views.append({
|
|
'video': video,
|
|
'views_count': views_count
|
|
})
|
|
|
|
popular_videos = [
|
|
{
|
|
'video': video[0],
|
|
'views_count': video[1]
|
|
}
|
|
for video in db.session.query(Video, func.count(Views.id).label('views_count'))
|
|
.outerjoin(Views, Views.video_id == Video.id)
|
|
.group_by(Video.id)
|
|
.order_by(func.count(Views.id).desc(), Video.cookie_votes.desc())
|
|
.limit(8)
|
|
.all()
|
|
]
|
|
|
|
most_viewed_videos = [
|
|
{
|
|
'video': video[0],
|
|
'views_count': video[1]
|
|
}
|
|
for video in db.session.query(Video, func.count(Views.id).label('views_count'))
|
|
.outerjoin(Views, Views.video_id == Video.id)
|
|
.group_by(Video.id)
|
|
.order_by(func.count(Views.id).desc())
|
|
.limit(8)
|
|
.all()
|
|
]
|
|
|
|
videos_tags = [video.tags for video in Video.query.all() if video.tags]
|
|
all_tags = [tag.strip() for tags in videos_tags for tag in tags.split(',')]
|
|
sorted_tags = sorted(set(all_tags))
|
|
|
|
user_cookies = 0
|
|
if current_user.is_authenticated:
|
|
user_cookies_record = Cookies.query.filter_by(username=current_user.username).first()
|
|
user_cookies = user_cookies_record.cookies if user_cookies_record else 0
|
|
|
|
return render_template(
|
|
'videos.html',
|
|
videos=videos_with_views,
|
|
pagination=pagination,
|
|
user_cookies=user_cookies,
|
|
search_query=search_query,
|
|
content_type='video',
|
|
tags=sorted_tags,
|
|
popular_videos=popular_videos,
|
|
most_viewed_videos=most_viewed_videos
|
|
)
|
|
|
|
|
|
@app.route('/vote_video/<int:video_id>', methods=['POST'])
|
|
@login_required
|
|
def vote_video(video_id):
|
|
video = Video.query.get_or_404(video_id)
|
|
user_cookies = Cookies.query.filter_by(username=current_user.username).first()
|
|
|
|
if video.username == current_user.username:
|
|
return redirect(url_for('view', content_type='video', id=video_id))
|
|
|
|
if user_cookies and user_cookies.cookies > 0:
|
|
existing_vote = VideoVotes.query.filter_by(username=current_user.username, video_id=video_id).first()
|
|
if not existing_vote:
|
|
user_cookies.cookies -= 1
|
|
video.cookie_votes += 1
|
|
new_vote = VideoVotes(username=current_user.username, video_id=video.id)
|
|
db.session.add(new_vote)
|
|
db.session.commit()
|
|
|
|
return redirect(url_for('view', content_type='video', id=video_id))
|
|
|
|
@app.route('/comics')
|
|
def comics():
|
|
page = request.args.get('page', 1, type=int)
|
|
search_query = request.args.get('search')
|
|
|
|
subscriptions = []
|
|
if current_user.is_authenticated:
|
|
subscriptions = [sub.author_id for sub in Subscription.query.filter_by(user_id=current_user.id).all()]
|
|
|
|
query = get_content_query(Comic, subscriptions, search_query)
|
|
pagination = query.paginate(page=page, per_page=10, error_out=False)
|
|
|
|
user_cookies = 0
|
|
if current_user.is_authenticated:
|
|
user_cookies_record = Cookies.query.filter_by(username=current_user.username).first()
|
|
user_cookies = user_cookies_record.cookies if user_cookies_record else 0
|
|
|
|
return render_template(
|
|
'comics.html',
|
|
comics=pagination.items,
|
|
pagination=pagination,
|
|
user_cookies=user_cookies,
|
|
search_query=search_query,
|
|
content_type='comic'
|
|
)
|
|
|
|
@app.route('/vote_comic/<int:comic_id>', methods=['POST'])
|
|
@login_required
|
|
def vote_comic(comic_id):
|
|
comic = Comic.query.get_or_404(comic_id)
|
|
user_cookies = Cookies.query.filter_by(username=current_user.username).first()
|
|
|
|
if comic.username == current_user.username:
|
|
return redirect(url_for('view', content_type='comic', id=comic_id))
|
|
|
|
if user_cookies and user_cookies.cookies > 0:
|
|
existing_vote = ComicVotes.query.filter_by(username=current_user.username, comic_id=comic_id).first()
|
|
if not existing_vote:
|
|
user_cookies.cookies -= 1
|
|
comic.cookie_votes += 1
|
|
new_vote = ComicVotes(username=current_user.username, comic_id=comic.id)
|
|
db.session.add(new_vote)
|
|
db.session.commit()
|
|
|
|
return redirect(url_for('view', content_type='comic', id=comic_id))
|
|
|
|
@app.route('/comic_edit/<int:comic_id>', methods=['GET', 'POST'])
|
|
@login_required
|
|
async def comic_edit(comic_id):
|
|
comic = Comic.query.get_or_404(comic_id)
|
|
|
|
if comic.username != current_user.username:
|
|
return redirect(url_for('index'))
|
|
|
|
cfp = os.path.join(app.config['UPLOAD_FOLDER']['comics'], comic.name)
|
|
if not os.path.exists(cfp):
|
|
os.makedirs(cfp)
|
|
|
|
form = EmptyForm()
|
|
|
|
if request.method == 'POST' and form.validate_on_submit():
|
|
action = request.form.get('action')
|
|
|
|
if action == 'delete' and (page_id := request.form.get('page')):
|
|
page = ComicPage.query.get(page_id)
|
|
if page:
|
|
os.remove(page.file_path.replace('\\', '/'))
|
|
db.session.delete(page)
|
|
db.session.commit()
|
|
pages = ComicPage.query.filter_by(comic_id=comic.id).order_by(ComicPage.page_number).all()
|
|
for i, page in enumerate(pages, start=1):
|
|
page.page_number = i
|
|
db.session.commit()
|
|
|
|
elif action == 'update' and (page_id := request.form.get('page')) and 'new_page' in request.files:
|
|
new_page = request.files['new_page']
|
|
filename = f"{uuid.uuid4().hex}.webp"
|
|
file_path = os.path.join(cfp, filename).replace('\\', '/')
|
|
webp_image = await convert_to_webp(new_page)
|
|
async with aiofiles.open(file_path, 'wb') as f:
|
|
await f.write(webp_image.read())
|
|
|
|
page = ComicPage.query.get(page_id)
|
|
if page:
|
|
os.remove(page.file_path.replace('\\', '/'))
|
|
page.file_path = file_path
|
|
db.session.commit()
|
|
|
|
elif action == 'add' and 'new_page' in request.files:
|
|
new_page = request.files['new_page']
|
|
filename = f"{uuid.uuid4().hex}.webp"
|
|
file_path = os.path.join(cfp, filename).replace('\\', '/')
|
|
webp_image = await convert_to_webp(new_page)
|
|
async with aiofiles.open(file_path, 'wb') as f:
|
|
await f.write(webp_image.read())
|
|
|
|
page_number = (db.session.query(db.func.max(ComicPage.page_number)).filter_by(comic_id=comic.id).scalar() or 0) + 1
|
|
db.session.add(ComicPage(comic_id=comic.id, page_number=page_number, file_path=file_path))
|
|
db.session.commit()
|
|
|
|
return redirect(url_for('comic_edit', comic_id=comic.id))
|
|
|
|
comic_pages = ComicPage.query.filter_by(comic_id=comic.id).order_by(ComicPage.page_number).all()
|
|
return render_template('comic_edit.html', comic=comic, comic_pages=comic_pages, form=form)
|
|
|
|
def update_related_tables(old_username, new_username):
|
|
|
|
Comments.query.filter_by(username=old_username).update({"username": new_username})
|
|
|
|
Image.query.filter_by(username=old_username).update({"username": new_username})
|
|
|
|
Video.query.filter_by(username=old_username).update({"username": new_username})
|
|
|
|
Comic.query.filter_by(username=old_username).update({"username": new_username})
|
|
|
|
Cookies.query.filter_by(username=old_username).update({"username": new_username})
|
|
|
|
Post.query.filter_by(username=old_username).update({"username": new_username})
|
|
|
|
Subscription.query.filter_by(user_id=old_username).update({"user_id": new_username})
|
|
Subscription.query.filter_by(author_id=old_username).update({"author_id": new_username})
|
|
|
|
UserItem.query.filter_by(username=old_username).update({"username": new_username})
|
|
|
|
db.session.commit()
|
|
|
|
@app.route('/profile_edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
def profile_edit():
|
|
if request.method == 'POST':
|
|
section = request.form.get('section')
|
|
|
|
if section == 'avatar_banner':
|
|
|
|
avatar = request.files.get('avatar')
|
|
banner = request.files.get('banner')
|
|
|
|
if avatar and allowed_file(avatar.filename, app.config['ALLOWED_IMAGE_EXTENSIONS']):
|
|
if check_file_size(avatar, app.config['MAX_IMAGE_SIZE']):
|
|
avatar_filename = secure_filename(avatar.filename)
|
|
avatar_path = os.path.join(app.config['UPLOAD_FOLDER']['avatars'], avatar_filename)
|
|
avatar.save(avatar_path)
|
|
current_user.avatar_file = avatar_filename
|
|
else:
|
|
return redirect(url_for('profile_edit'))
|
|
|
|
if banner and allowed_file(banner.filename, app.config['ALLOWED_IMAGE_EXTENSIONS']):
|
|
if check_file_size(banner, app.config['MAX_IMAGE_SIZE']):
|
|
banner_filename = secure_filename(banner.filename)
|
|
banner_path = os.path.join(app.config['UPLOAD_FOLDER']['banners'], banner_filename)
|
|
banner.save(banner_path)
|
|
current_user.banner_file = banner_filename
|
|
else:
|
|
return redirect(url_for('profile_edit'))
|
|
|
|
db.session.commit()
|
|
|
|
return redirect(url_for('profile', username=current_user.username))
|
|
|
|
elif section == 'username':
|
|
|
|
new_username = request.form.get('new_username')
|
|
if new_username and new_username != current_user.username:
|
|
if len(new_username) < 3 or len(new_username) > 20:
|
|
pass
|
|
elif User.query.filter_by(username=new_username).first():
|
|
pass
|
|
else:
|
|
old_username = current_user.username
|
|
current_user.username = new_username
|
|
update_related_tables(old_username, new_username)
|
|
return redirect(url_for('profile', username=new_username))
|
|
|
|
elif section == 'bio':
|
|
|
|
bio = request.form.get('bio')
|
|
current_user.bio = bio
|
|
db.session.commit()
|
|
|
|
elif section == 'password':
|
|
|
|
current_password = request.form.get('current_password')
|
|
new_password = request.form.get('new_password')
|
|
confirm_password = request.form.get('confirm_new_password')
|
|
|
|
if not current_user.check_password(current_password):
|
|
pass
|
|
elif new_password != confirm_password:
|
|
pass
|
|
elif len(new_password) < 6:
|
|
pass
|
|
else:
|
|
current_user.encrypted_password = bcrypt.generate_password_hash(new_password).decode('utf-8')
|
|
db.session.commit()
|
|
return redirect(url_for('profile_edit'))
|
|
|
|
elif section == 'create_post':
|
|
|
|
post_text = request.form.get('post_text')
|
|
post_media = request.files.get('post_media')
|
|
|
|
if post_text:
|
|
new_post = Post(
|
|
username=current_user.username,
|
|
text=post_text
|
|
)
|
|
db.session.add(new_post)
|
|
|
|
if post_media and allowed_file(post_media.filename, app.config['ALLOWED_IMAGE_EXTENSIONS']):
|
|
if check_file_size(post_media, app.config['MAX_IMAGE_SIZE']):
|
|
media_filename = secure_filename(post_media.filename)
|
|
media_path = os.path.join(app.config['UPLOAD_FOLDER']['posts'], media_filename)
|
|
post_media.save(media_path)
|
|
new_post.media_file = media_filename
|
|
db.session.commit()
|
|
pass
|
|
else:
|
|
return redirect(url_for('profile_edit'))
|
|
|
|
db.session.commit()
|
|
return redirect(url_for('user_posts', username=current_user.username))
|
|
|
|
elif section == 'decoration':
|
|
selected_item_id = request.form.get('selected_item')
|
|
if selected_item_id:
|
|
|
|
selected_item = UserItem.query.filter_by(username=current_user.username, item_id=selected_item_id).first()
|
|
|
|
if selected_item:
|
|
|
|
current_user.current_item = selected_item.item.id
|
|
db.session.commit()
|
|
pass
|
|
else:
|
|
pass
|
|
return redirect(url_for('profile', username=current_user.username))
|
|
|
|
db.session.commit()
|
|
return redirect(url_for('profile_edit'))
|
|
|
|
return render_template(
|
|
'profile_edit.html',
|
|
user=current_user,
|
|
user_items=UserItem.query.filter_by(username=current_user.username).all()
|
|
)
|
|
|
|
@app.route('/profile/<username>')
|
|
def profile(username):
|
|
user = User.query.filter_by(username=username).first_or_404()
|
|
|
|
if not user.avatar_file:
|
|
user.avatar_file = 'default-avatar.png'
|
|
|
|
if not user.banner_file:
|
|
user.banner_file = 'default-banner.png'
|
|
|
|
total_arts = Image.query.filter_by(username=username).count()
|
|
total_videos = Video.query.filter_by(username=username).count()
|
|
total_comics = Comic.query.filter_by(username=username).count()
|
|
|
|
arts = Image.query.filter_by(username=username).order_by(Image.cookie_votes.desc(), Image.publication_date.desc()).limit(3).all()
|
|
videos = Video.query.filter_by(username=username).order_by(Video.cookie_votes.desc(), Video.publication_date.desc()).limit(3).all()
|
|
comics = Comic.query.filter_by(username=username).order_by(Comic.cookie_votes.desc(), Comic.publication_date.desc()).limit(3).all()
|
|
|
|
subscribed = (
|
|
current_user.is_authenticated and
|
|
Subscription.query.filter_by(user_id=current_user.id, author_id=user.id).first() is not None
|
|
)
|
|
|
|
current_item = Item.query.get(user.current_item) if user.current_item else None
|
|
|
|
is_current_user = current_user.is_authenticated and current_user.username == user.username
|
|
|
|
subscriptions_count = Subscription.query.filter_by(author_id=user.id).count()
|
|
|
|
return render_template(
|
|
'profile.html',
|
|
user=user,
|
|
arts=arts,
|
|
videos=videos,
|
|
comics=comics,
|
|
total_arts=total_arts,
|
|
total_videos=total_videos,
|
|
total_comics=total_comics,
|
|
current_item=current_item,
|
|
is_current_user=is_current_user,
|
|
subscribed=subscribed,
|
|
subscriptions_count=subscriptions_count
|
|
)
|
|
|
|
@app.route('/posts/<username>')
|
|
def user_posts(username):
|
|
user = User.query.filter_by(username=username).first_or_404()
|
|
posts = Post.query.filter_by(username=username).order_by(Post.post_date.desc()).all()
|
|
avatars = {user.username: user.avatar_file for user in User.query.all()}
|
|
return render_template('post.html', username=username, posts=posts, avatars=avatars)
|
|
|
|
@app.route('/posts/<username>/add_comment/<int:post_id>', methods=['POST'])
|
|
@login_required
|
|
def add_comment(username, post_id):
|
|
comment_text = request.form.get('comment_text')
|
|
if not comment_text:
|
|
return redirect(url_for('user_posts', username=username))
|
|
|
|
comment = Comments(
|
|
username=current_user.username,
|
|
post_id=post_id,
|
|
comment_text=comment_text,
|
|
comment_date=datetime.utcnow()
|
|
)
|
|
db.session.add(comment)
|
|
db.session.commit()
|
|
return redirect(url_for('user_posts', username=username))
|
|
|
|
@app.route('/subscribe/<int:author_id>', methods=['POST'])
|
|
@login_required
|
|
def subscribe(author_id):
|
|
if author_id == current_user.id:
|
|
return redirect(url_for('profile', username=current_user.username))
|
|
|
|
existing_subscription = Subscription.query.filter_by(user_id=current_user.id, author_id=author_id).first()
|
|
if existing_subscription:
|
|
return redirect(url_for('profile', username=current_user.username))
|
|
|
|
new_subscription = Subscription(user_id=current_user.id, author_id=author_id)
|
|
db.session.add(new_subscription)
|
|
db.session.commit()
|
|
|
|
return redirect(url_for('profile', username=current_user.username))
|
|
|
|
@app.route('/unsubscribe/<int:author_id>', methods=['POST'])
|
|
@login_required
|
|
def unsubscribe(author_id):
|
|
|
|
subscription = Subscription.query.filter_by(
|
|
user_id=current_user.id, author_id=author_id).first()
|
|
if subscription:
|
|
db.session.delete(subscription)
|
|
db.session.commit()
|
|
return redirect(url_for('profile', username=User.query.get(author_id).username))
|
|
|
|
@app.route('/delete_post/<int:post_id>', methods=['POST'])
|
|
@login_required
|
|
def delete_post(post_id):
|
|
try:
|
|
validate_csrf(request.form.get('csrf_token'))
|
|
except BadRequest:
|
|
return redirect(url_for('user_posts', username=current_user.username))
|
|
|
|
post = Post.query.get_or_404(post_id)
|
|
if post.username != current_user.username:
|
|
abort(403)
|
|
|
|
if post.media_file:
|
|
media_path = os.path.join(app.config['UPLOAD_FOLDER']['posts'], post.media_file)
|
|
if os.path.exists(media_path):
|
|
os.remove(media_path)
|
|
db.session.delete(post)
|
|
db.session.commit()
|
|
return redirect(url_for('user_posts', username=current_user.username))
|
|
|
|
@app.route('/privacy_policy')
|
|
def privacy_policy():
|
|
return render_template('privacy_policy.html')
|
|
|
|
@app.route('/terms_of_use')
|
|
def terms_of_use():
|
|
return render_template('terms_of_use.html')
|
|
|
|
@app.route('/publication_rules')
|
|
def publication_rules():
|
|
return render_template('publication_rules.html')
|
|
|
|
@app.context_processor
|
|
def inject_forms():
|
|
return dict(form=RegistrationForm())
|
|
|
|
if __name__ == '__main__':
|
|
with app.app_context():
|
|
db.create_all()
|
|
app.run(debug=True) |