365 lines
8.4 KiB
Python
Executable File
365 lines
8.4 KiB
Python
Executable File
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('/file/<path:filename>')
|
|
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/<path:permalink>')
|
|
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/<path:permalink>')
|
|
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('/')
|
|
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)
|
|
select = """
|
|
SELECT
|
|
post
|
|
FROM
|
|
post
|
|
WHERE
|
|
hidden = ? AND
|
|
saved = ?
|
|
ORDER BY
|
|
created_utc asc
|
|
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)
|
|
|
|
@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)
|
|
select = """
|
|
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
|
|
created_utc asc
|
|
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)
|
|
|
|
@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)
|
|
select = """
|
|
SELECT
|
|
post
|
|
FROM
|
|
post
|
|
WHERE
|
|
saved = ?
|
|
ORDER BY
|
|
created_utc desc
|
|
LIMIT ?
|
|
"""
|
|
binds = [True, 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, saved=True)
|
|
|
|
|
|
@app.route('/r/<path:subreddit>')
|
|
def get_subreddit(subreddit):
|
|
title = f"/r/{subreddit}"
|
|
connection = sqlite3.connect(config.db_file)
|
|
cursor = connection.cursor()
|
|
sidebar_links = get_sidebar_links(cursor)
|
|
select = """
|
|
SELECT
|
|
post
|
|
FROM
|
|
post
|
|
WHERE
|
|
subreddit = ? AND
|
|
hidden = ? AND
|
|
saved = ?
|
|
ORDER BY
|
|
created_utc asc
|
|
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)
|
|
|
|
def get_sidebar_links(cursor):
|
|
select = """
|
|
SELECT
|
|
subreddit
|
|
FROM
|
|
(
|
|
SELECT
|
|
subreddit,
|
|
count(*) count
|
|
FROM
|
|
post
|
|
WHERE
|
|
hidden = ?
|
|
GROUP BY
|
|
subreddit
|
|
ORDER BY
|
|
count desc
|
|
) t
|
|
WHERE
|
|
count > ?
|
|
"""
|
|
binds = [False, config.other_posts_cutoff]
|
|
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", "<br>")
|
|
post["body"] = re.sub(r"\[(.*?)\]\((.*?)\)", r'<b><a href="\2" style="white-space: nowrap;" class="no-style-link">\1</a></b>', 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 '<img class="invertible" src="/file/{0}" {1}>'.format(file, 'fetchpriority="high" loading="eager"' if priority else '')
|
|
if file.find("_AUDIO_")>0:
|
|
return '<audio src="/file/{0}" hidden></audio>'.format(file)
|
|
if file.endswith('.mp4'):
|
|
return '<video src="/file/{0}" type="video/mp4" onplay="playAudio(this)" onpause="pauseAudio(this)" onseeked="seekAudio(this)" controls></video>'.format(file)
|
|
if file.endswith('.webm'):
|
|
return '<video src="/file/{0}" type="video/webm" controls></video>'.format(file)
|
|
return file
|
|
|
|
if __name__ == '__main__':
|
|
subprocess.run(["python3", "make_db.py"])
|
|
app.run(host='0.0.0.0', port=8000)
|