from flask import Flask, send_file, render_template, request from urllib.parse import urlparse import delete_posts import config import json import re import sqlite3 import subprocess import time app = Flask(__name__) @app.route('/') def index(): connection = sqlite3.connect(config.db_file) cursor = connection.cursor() select = """ SELECT count(*) FROM subreddit """ count = cursor.execute(select).fetchone()[0] if count == 0: return admin() return front_page() @app.route('/admin', methods=['GET', 'POST', 'DELETE']) def admin(): connection = sqlite3.connect(config.db_file) cursor = connection.cursor() if request.method == 'DELETE': type = request.args.get("type") if type == "sub": delete = """ DELETE FROM subreddit WHERE subreddit = ? """ elif type == "block": delete = """ DELETE FROM block WHERE name = ? """ else: connection.close() return "" binds = [request.args.get("name")] cursor.execute(delete, binds) connection.commit() connection.close() return "" elif request.method == 'POST': type = request.form.get("type") if type == "sub": upsert = """ INSERT INTO subreddit (subreddit, minimum_score, fetch_by, fetch_max) VALUES (?, ?, ?, ?) ON CONFLICT (subreddit) DO UPDATE SET minimum_score=excluded.minimum_score, fetch_by=excluded.fetch_by, fetch_max=excluded.fetch_max """ binds = [ request.form.get("name"), int(request.form.get("score")), request.form.get("by"), int(request.form.get("max")) ] elif type == "block": upsert = """ INSERT OR IGNORE INTO block (name) VALUES (?) """ binds = [request.form.get("name")] else: connection.close() return "" cursor.execute(upsert, binds) connection.commit() delete_posts.run() sidebar_links = get_sidebar_links(cursor) select = """ SELECT subreddit, minimum_score, fetch_by, fetch_max FROM subreddit """ subreddits = cursor.execute(select).fetchall() select = """ SELECT name FROM block """ rows = cursor.execute(select).fetchall() blocks = [row[0] for row in rows] connection.close() return render_template('admin.html', sidebar_links=sidebar_links, subreddits=subreddits, blocks=blocks) @app.route('/r/all') def front_page(): title = "/r/all" connection = sqlite3.connect(config.db_file) cursor = connection.cursor() sidebar_links = get_sidebar_links(cursor) right_handed = get_config('side')!='left' sort = get_config("sort") or "created_utc asc" select = f""" SELECT post FROM post WHERE hidden = ? AND saved = ? ORDER BY {sort} LIMIT ? """ binds = [False, False, config.posts_per_page_load] posts = get_posts_from_select(cursor, select, binds) connection.close() return render_template( 'index.html', title=title, posts=posts, sidebar_links=sidebar_links, sort=sort.split(), right_handed=right_handed) @app.route('/r/other') def other_page(): title = "/r/other" connection = sqlite3.connect(config.db_file) cursor = connection.cursor() sidebar_links = get_sidebar_links(cursor) right_handed = get_config('side')!='left' sort = get_config("sort") or "created_utc asc" select = f""" SELECT post FROM post WHERE hidden = ? AND saved = ? AND subreddit IN ( SELECT subreddit FROM ( SELECT subreddit, count(*) count FROM post WHERE hidden = ? AND saved = ? GROUP BY subreddit ) t WHERE count <= ? ) ORDER BY {sort} LIMIT ? """ binds = [False, False, False, False, config.other_posts_cutoff, config.posts_per_page_load] posts = get_posts_from_select(cursor, select, binds) connection.close() return render_template( 'index.html', title=title, posts=posts, sidebar_links=sidebar_links, sort=sort.split(), right_handed=right_handed) @app.route('/r/saved') def get_saved(): title = "/r/saved" connection = sqlite3.connect(config.db_file) cursor = connection.cursor() sidebar_links = get_sidebar_links(cursor) right_handed = get_config('side')!='left' sort = get_config("sort") or "created_utc desc" select = f""" SELECT post FROM post WHERE saved = ? ORDER BY {sort} """ binds = [True] posts = get_posts_from_select(cursor, select, binds) connection.close() return render_template( 'index.html', title=title, posts=posts, sidebar_links=sidebar_links, saved=True, sort=sort.split(), right_handed=right_handed) @app.route('/r/') def get_subreddit(subreddit): title = f"/r/{subreddit}" connection = sqlite3.connect(config.db_file) cursor = connection.cursor() sidebar_links = get_sidebar_links(cursor) right_handed = get_config('side')!='left' sort = get_config("sort") or "created_utc asc" select = f""" SELECT post FROM post WHERE subreddit = ? AND hidden = ? AND saved = ? ORDER BY {sort} LIMIT ? """ binds = [subreddit, False, False, config.posts_per_page_load] posts = get_posts_from_select(cursor, select, binds) connection.close() return render_template( 'index.html', title=title, posts=posts, sidebar_links=sidebar_links, sort=sort.split(), right_handed=right_handed) @app.route('/file/') def serve_file(filename): try: return send_file('{0}/{1}'.format(config.media_dir, filename), as_attachment=False) except FileNotFoundError: return "File not found", 404 @app.route('/hide/') def hide_post(permalink): if permalink[0] != "/": permalink = "/" + permalink connection = sqlite3.connect(config.db_file) cursor = connection.cursor() update = """ UPDATE post SET hidden = ? WHERE permalink = ? AND saved = ? """ binds = [True, permalink, False] cursor.execute(update,binds) connection.commit() connection.close() return "" @app.route('/save/') def save_post(permalink): if permalink[0] != "/": permalink = "/" + permalink connection = sqlite3.connect(config.db_file) cursor = connection.cursor() action = request.args.get("action") update = """ UPDATE post SET saved = ? WHERE permalink = ? """ binds = [(action == "save"), permalink] cursor.execute(update,binds) connection.commit() connection.close() return "" @app.route('/config', methods=['POST']) def save_config(): connection = sqlite3.connect(config.db_file) cursor = connection.cursor() key = request.args.get("key") value = request.args.get("value") insert = f""" INSERT INTO config (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value=excluded.value """ binds = [key, value] cursor.execute(insert, binds) connection.commit() connection.close() return "" def get_config(key): connection = sqlite3.connect(config.db_file) cursor = connection.cursor() select = """ SELECT value FROM config WHERE key = ? """ binds = [key] result = cursor.execute(select, binds).fetchone() if result is None: return None return result[0] def get_sidebar_links(cursor): select = """ SELECT subreddit FROM ( SELECT subreddit, count(*) count FROM post WHERE hidden = ? AND saved = ? GROUP BY subreddit ORDER BY count desc ) t LIMIT ? """ binds = [False, False, config.top_subreddits] results = cursor.execute(select, binds).fetchall() links = [f"/r/{sub[0]}" for sub in results] links.insert(0, "/r/all") links.append("/r/other") links.append("/r/saved") links.append("/admin") return links def get_posts_from_select(cursor, select, binds): results = cursor.execute(select, binds).fetchall() posts = [json.loads(post[0]) for post in results] add_media_html_to_posts(posts) add_subreddits_to_posts(posts) add_age_to_posts(posts) reformat_body(posts) return posts def add_media_html_to_posts(posts): for post_index, post in enumerate(posts): media_html = [] for media_index, media in enumerate(post["media_urls"]): filename = urlparse(media).path if filename[0]=='/': filename = filename[1:] html = get_media_html(filename, True if (post_index < 3 and media_index == 0) else False) media_html.append(html) post["media_html"] = media_html def add_subreddits_to_posts(posts): # todo, remove after 30 days once subreddit is naturally a part of the post data for post in posts: if "subreddit" not in post: m = re.search(r"\/r\/([a-zA-Z0-9_]+)\/.*", post["permalink"]) post["subreddit"] = m.group(1) def add_age_to_posts(posts): seconds = time.time() for post in posts: diff = seconds - int(post["created_utc"]) if diff < 3600: post["age"] = str(int(diff//60))+'m' elif diff < (3600 * 24): post["age"] = str(int(diff//3600))+'h' else: post["age"] = str(int(diff//(3600*24)))+'d' def reformat_body(posts): for post in posts: if "body" in post and post["body"] is not None: post["body"] = post["body"].rstrip().replace("\n", "
") post["body"] = re.sub(r"\[(.*?)\]\((.*?)\)", r'\1', post["body"]) def get_media_html(file, priority=False): if file.endswith('.jpg') or file.endswith('.jpeg') or file.endswith('.png') or file.endswith('.gif'): return ''.format(file, 'fetchpriority="high" loading="eager"' if priority else '') if file.find("_AUDIO_")>0: return ''.format(file) if file.endswith('.mp4'): return f""" """ if file.endswith('.webm'): return ''.format(file) return file if __name__ == '__main__': subprocess.run(["python3", "make_db.py"]) app.run(host='0.0.0.0', port=8000)