import re import sqlite3 from flask_login import current_user, login_required from flask import Blueprint, render_template, request, redirect, flash from ..common.common import * frontend = Blueprint('reddit', __name__, template_folder='templates', static_folder='static', static_url_path='/static/rd') @frontend.route('/feed/subreddits') @frontend.route('/r/') def reddit(subreddit=None): token = getattr(current_user, 'token', 'guest') count = int(request.args.get('count', 0)) before = request.args.get('before') after = request.args.get('after') sortorder = request.args.get('s', "hot") # TODO: verify! timerange = request.args.get('t', None) # TODO: verify! all_subreddits = get_subreddits(token) subreddits = [subreddit] if subreddit else all_subreddits try: data = fetch_reddit(subreddits, sorted_by=sortorder, time=timerange, limit=36, count=count, before=before, after=after) videos = parse_reddit_videos(data) before = data['data']['before'] after = data['data']['after'] except RedditException as e: return f"error retrieving reddit data: {e}", 502 # TODO: better # set pin/hide stati of retrieved videos with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() c.execute(""" SELECT post_id,display,video_id,title,subreddit FROM reddit_flags LEFT JOIN reddit_posts ON reddit_posts.id = reddit_flags.post_id WHERE user = ? AND display IS NOT NULL """, (token,)) flags = c.fetchall() # on user's feed: show all pinned videos # on /r/...: only show pinned videos that are on the page anyways if not subreddit: # only on /feed/reddit videos = [ # prepend pinned posts { 'video_id': video_id, 'title': title, 'url': f"https://old.reddit.com/r/{sr}/comments/{post_id}/", 'n_comments': '?', 'n_karma': '?', 'subreddit': sr, 'post_id': post_id, 'pinned': True, } for post_id,display,video_id,title,sr in flags if display == 'pinned' ] + [ # followed by non-pinned, non-hidden videos v for v in videos if v['post_id'] not in [post for post,_,_,_,_ in flags] ] else: # on /r/... pinned = [post for post,disp,_,_,_ in flags if disp == 'pinned'] hidden = [post for post,disp,_,_,_ in flags if disp == 'hidden'] videos = sorted([ {**v, 'pinned': v['post_id'] in pinned} for v in videos if v['post_id'] not in hidden ], key=lambda e: e['data']['score'] > 1, reverse=True) title = f"/r/{subreddit}" if subreddit else "my subreddits" return render_template('reddit.html.j2', title=title, rows=videos, subreddits=all_subreddits, before=before, after=after, count=count) @frontend.route('/manage/subreddits') # disabled for guest user: @login_required def subscription_manager(): token = getattr(current_user, 'token', 'guest') subreddits = get_subreddits(token) return render_template('subreddit_manager.html.j2', subreddits=subreddits) @frontend.route('/feed/subreddits', methods=['POST']) @frontend.route('/r/', methods=['POST']) @login_required def feed_post(subreddit=None): token = current_user.token action = next(request.form.keys(), None) if action in ['pin', 'unpin', 'hide']: post_id = request.form.get(action) if not re.match(r"^[a-z0-9]+$", post_id): return "invalid post id", 400 display = { 'pin': 'pinned', 'unpin': None, 'hide': 'hidden', }[action] with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() # if the post was pinned, we need to be able to retrieve it # independently from if it is shown in the feed. so we check if we # know about it, or download and store it. if action == "pin": c.execute(""" SELECT count(*) FROM reddit_posts WHERE id = ? """, (post_id,)) if c.fetchone()[0] < 1: post = parse_reddit_videos(fetch_reddit_post(post_id))[0] # TODO: if exception, abort pinning c.execute(""" INSERT INTO reddit_posts (id,subreddit,title,video_id) VALUES (?,?,?,?) """, ( post['post_id'], post['subreddit'], html.unescape(post['title']), post['video_id'] )) c.execute(""" INSERT OR REPLACE INTO reddit_flags (user, post_id, display) VALUES (?, ?, ?) """, (token, post_id, display)) else: flash("unsupported action", "error") return redirect(request.url, code=303) @frontend.route('/manage/subreddits', methods=['POST']) @login_required def manage_subscriptions(): token = current_user.token if 'subscribe' in request.form: subreddit = request.form.get("subscribe") match = re.search(r"(?:(?:https?://)?(?:old.|www.|\w\w.)?reddit.com)?(?:/?r/)?([-+_0-9A-Za-z]{2,21})", subreddit) if match: subreddit = match.group(1) else: flash("invalid subreddit", "error") return redirect(request.url, code=303) with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() c.execute(""" INSERT OR IGNORE INTO subreddits (user, subreddit) VALUES (?, ?) """, (token, subreddit)) elif 'unsubscribe' in request.form: subreddit = request.form.get("unsubscribe") with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() c.execute(""" DELETE FROM subreddits WHERE user = ? AND subreddit = ? """, (token, subreddit)) # TODO: sql-error-handling, report success else: flash("unsupported action", "error") return redirect(request.url, code=303) def get_subreddits(token): with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() c.execute(""" SELECT subreddit FROM subreddits WHERE user = ? ORDER BY subreddit COLLATE NOCASE ASC """, (token,)) subreddits = [sr for (sr,) in c.fetchall()] return subreddits @frontend.app_template_filter('trim3') def trim3(n): if type(n) != int: return n # not a number elif round(n, 1) >= 10_000: return "%.0fk" % (n/1000) elif n >= 1_000: return "%.1fk" % (n/1000) else: return "%d" % n