Twitter follow logic and DB rewrite + Youtube logic improvements
This commit is contained in:
parent
149643721e
commit
41e78f36f8
@ -13,6 +13,11 @@ channel_association = db.Table('channel_association',
|
||||
db.Column('user_id', db.Integer, db.ForeignKey('user.id'))
|
||||
) # Association: CHANNEL --followed by--> [USERS]
|
||||
|
||||
twitter_association = db.Table('twitter_association',
|
||||
db.Column('account_id', db.String, db.ForeignKey('twitterAccount.id')),
|
||||
db.Column('user_id', db.Integer, db.ForeignKey('user.id'))
|
||||
) # Association: ACCOUNT --followed by--> [USERS]
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(64), index=True, unique=True)
|
||||
@ -48,12 +53,27 @@ class User(UserMixin, db.Model):
|
||||
def saved_posts(self):
|
||||
return Post.query.filter_by(user_id=self.id)
|
||||
|
||||
# TWITTER
|
||||
def twitter_following_list(self):
|
||||
return self.twitterFollowed.all()
|
||||
|
||||
def is_following_tw(self, uname):
|
||||
temp_cid = twitterFollow.query.filter_by(username = uname).first()
|
||||
if temp_cid is None:
|
||||
return False
|
||||
else:
|
||||
following = self.twitter_following_list()
|
||||
for f in following:
|
||||
if f.username == uname:
|
||||
return True
|
||||
return False
|
||||
|
||||
# YOUTUBE
|
||||
def youtube_following_list(self):
|
||||
return self.youtubeFollowed.all()
|
||||
|
||||
def is_following_yt(self, cid):
|
||||
temp_cid = invidiousFollow.query.filter_by(channelId = cid).first()
|
||||
temp_cid = youtubeFollow.query.filter_by(channelId = cid).first()
|
||||
if temp_cid is None:
|
||||
return False
|
||||
else:
|
||||
@ -69,11 +89,16 @@ class User(UserMixin, db.Model):
|
||||
secondaryjoin=(followers.c.followed_id == id),
|
||||
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
|
||||
|
||||
youtubeFollowed = db.relationship("invidiousFollow",
|
||||
youtubeFollowed = db.relationship("youtubeFollow",
|
||||
secondary=channel_association,
|
||||
back_populates="followers",
|
||||
lazy='dynamic')
|
||||
|
||||
twitterFollowed = db.relationship("twitterFollow",
|
||||
secondary=twitter_association,
|
||||
back_populates="followers",
|
||||
lazy='dynamic')
|
||||
|
||||
|
||||
@login.user_loader
|
||||
def load_user(id):
|
||||
@ -106,16 +131,28 @@ class ytPost():
|
||||
id = 'isod'
|
||||
|
||||
|
||||
class invidiousFollow(db.Model):
|
||||
class youtubeFollow(db.Model):
|
||||
__tablename__ = 'channel'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
channelId = db.Column(db.String(30), nullable=False, unique=True)
|
||||
channelName = db.Column(db.String(30))
|
||||
followers = db.relationship('User',
|
||||
secondary=channel_association,
|
||||
back_populates="youtubeFollowed")
|
||||
|
||||
def __repr__(self):
|
||||
return '<invidiousFollow {}>'.format(self.channelId)
|
||||
return '<youtubeFollow {}>'.format(self.channelName)
|
||||
|
||||
class twitterFollow(db.Model):
|
||||
__tablename__ = 'twitterAccount'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(30), unique=True)
|
||||
followers = db.relationship('User',
|
||||
secondary=twitter_association,
|
||||
back_populates="twitterFollowed")
|
||||
|
||||
def __repr__(self):
|
||||
return '<twitterFollow {}>'.format(self.username)
|
||||
|
||||
class Post(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
200
app/routes.py
200
app/routes.py
@ -1,10 +1,11 @@
|
||||
from flask import render_template, flash, redirect, url_for, request, send_from_directory, Markup
|
||||
from app.forms import LoginForm, RegistrationForm, EmptyForm, SearchForm, ChannelForm
|
||||
from app.models import User, twitterPost, ytPost, Post, youtubeFollow, twitterFollow
|
||||
from flask_login import login_user, logout_user, current_user, login_required
|
||||
from app.models import User, twitterPost, ytPost, Post, invidiousFollow
|
||||
from flask import Flask, Response, stream_with_context
|
||||
from requests_futures.sessions import FuturesSession
|
||||
from concurrent.futures import as_completed
|
||||
from werkzeug.utils import secure_filename
|
||||
from youtube_search import YoutubeSearch
|
||||
from werkzeug.urls import url_parse
|
||||
from youtube_dl import YoutubeDL
|
||||
@ -24,6 +25,11 @@ nitterInstanceII = "https://nitter.mastodont.cat/"
|
||||
ytChannelRss = "https://www.youtube.com/feeds/videos.xml?channel_id="
|
||||
invidiousInstance = "invidio.us"
|
||||
|
||||
##########################
|
||||
#### Global variables ####
|
||||
##########################
|
||||
ALLOWED_EXTENSIONS = {'json'}
|
||||
|
||||
#########################
|
||||
#### Twitter Logic ######
|
||||
#########################
|
||||
@ -32,19 +38,19 @@ invidiousInstance = "invidio.us"
|
||||
@login_required
|
||||
def index():
|
||||
start_time = time.time()
|
||||
following = current_user.following_list()
|
||||
followed = current_user.followed.count()
|
||||
followingList = current_user.twitter_following_list()
|
||||
followCount = len(followingList)
|
||||
posts = []
|
||||
avatarPath = "img/avatars/1.png"
|
||||
form = EmptyForm()
|
||||
posts.extend(getFeed(following))
|
||||
posts.extend(getFeed(followingList))
|
||||
posts.sort(key=lambda x: x.timeStamp, reverse=True)
|
||||
if not posts:
|
||||
profilePic = avatarPath
|
||||
else:
|
||||
profilePic = posts[0].userProfilePic
|
||||
print("--- {} seconds fetching twitter feed---".format(time.time() - start_time))
|
||||
return render_template('index.html', title='Home', posts=posts, avatar=avatarPath, profilePic = profilePic, followedCount=followed, form=form)
|
||||
return render_template('index.html', title='Home', posts=posts, avatar=avatarPath, profilePic = profilePic, followedCount=followCount, form=form)
|
||||
|
||||
@app.route('/savePost/<url>', methods=['POST'])
|
||||
@login_required
|
||||
@ -86,71 +92,56 @@ def deleteSaved(id):
|
||||
def follow(username):
|
||||
form = EmptyForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(username=username).first()
|
||||
isTwitter = isTwitterUser(username)
|
||||
if user is None and isTwitter:
|
||||
x = ''.join(randomrandom.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16))
|
||||
newUser = User(username=username, email="{}@person.is".format(x))
|
||||
db.session.add(newUser)
|
||||
if twFollow(username):
|
||||
flash("{} followed!".format(username))
|
||||
else:
|
||||
flash("Something went wrong...")
|
||||
return redirect(request.referrer)
|
||||
|
||||
def twFollow(username):
|
||||
if isTwitterUser(username):
|
||||
try:
|
||||
follow = twitterFollow()
|
||||
follow.username = username
|
||||
follow.followers.append(current_user)
|
||||
db.session.add(follow)
|
||||
db.session.commit()
|
||||
flash('You are now following {}!'.format(username))
|
||||
#flash('User {} not found.'.format(username))
|
||||
return redirect(url_for('index'))
|
||||
if user == current_user:
|
||||
flash('You cannot follow yourself!')
|
||||
return redirect(url_for('user', username=username))
|
||||
current_user.follow(user)
|
||||
db.session.commit()
|
||||
flash('You are following {}!'.format(username))
|
||||
return redirect(url_for('user', username=username))
|
||||
return True
|
||||
except:
|
||||
flash("Couldn't follow {}. Maybe you are already following!".format(username))
|
||||
return False
|
||||
else:
|
||||
return redirect(url_for('index'))
|
||||
flash("Something went wrong... try again")
|
||||
return False
|
||||
|
||||
@app.route('/unfollow/<username>', methods=['POST'])
|
||||
@login_required
|
||||
def unfollow(username):
|
||||
form = EmptyForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user is None:
|
||||
flash('User {} not found.'.format(username))
|
||||
return redirect(url_for('index'))
|
||||
if user == current_user:
|
||||
flash('You cannot unfollow yourself!')
|
||||
return redirect(url_for('user', username=username))
|
||||
current_user.unfollow(user)
|
||||
db.session.commit()
|
||||
flash('You are no longer following {}.'.format(username))
|
||||
return redirect(url_for('user', username=username))
|
||||
else:
|
||||
return redirect(url_for('index'))
|
||||
if twUnfollow(username):
|
||||
flash("{} unfollowed!".format(username))
|
||||
else:
|
||||
flash("Something went wrong...")
|
||||
return redirect(request.referrer)
|
||||
|
||||
@app.route('/unfollowList/<username>', methods=['POST'])
|
||||
@login_required
|
||||
def unfollowList(username):
|
||||
form = EmptyForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user is None:
|
||||
flash('User {} not found.'.format(username))
|
||||
return redirect(url_for('index'))
|
||||
if user == current_user:
|
||||
flash('You cannot unfollow yourself!')
|
||||
return redirect(url_for('user', username=username))
|
||||
current_user.unfollow(user)
|
||||
def twUnfollow(username):
|
||||
try:
|
||||
user = twitterFollow.query.filter_by(username=username).first()
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
flash('You are no longer following {}!'.format(username))
|
||||
return redirect(url_for('following'))
|
||||
else:
|
||||
return redirect(url_for('index'))
|
||||
flash("{} unfollowed!".format(username))
|
||||
except:
|
||||
flash("There was an error unfollowing the user. Try again.")
|
||||
return redirect(request.referrer)
|
||||
|
||||
@app.route('/following')
|
||||
@login_required
|
||||
def following():
|
||||
form = EmptyForm()
|
||||
following = current_user.following_list()
|
||||
followed = current_user.followed.count()
|
||||
return render_template('following.html', accounts = following, count = followed, form = form)
|
||||
followCount = len(current_user.twitter_following_list())
|
||||
accounts = current_user.twitter_following_list()
|
||||
return render_template('following.html', accounts = accounts, count = followCount, form = form)
|
||||
|
||||
@app.route('/search', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@ -174,16 +165,9 @@ def search():
|
||||
@app.route('/user/<username>')
|
||||
@login_required
|
||||
def user(username):
|
||||
user = User.query.filter_by(username=username).first()
|
||||
isTwitter = isTwitterUser(username)
|
||||
|
||||
if isTwitter and user is None:
|
||||
x = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16))
|
||||
newUser = User(username=username, email="{}@person.is".format(x))
|
||||
db.session.add(newUser)
|
||||
db.session.commit()
|
||||
|
||||
elif not isTwitter and user is None:
|
||||
if not isTwitter:
|
||||
flash("This user is not on Twitter.")
|
||||
return redirect( url_for('error', errno="404"))
|
||||
|
||||
posts = []
|
||||
@ -202,13 +186,23 @@ def user(username):
|
||||
@app.route('/youtube', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def youtube():
|
||||
followCount = len(current_user.youtube_following_list())
|
||||
start_time = time.time()
|
||||
ids = current_user.youtube_following_list()
|
||||
videos = getYoutubePosts(ids)
|
||||
if videos:
|
||||
videos.sort(key=lambda x: x.date, reverse=True)
|
||||
print("--- {} seconds fetching youtube feed---".format(time.time() - start_time))
|
||||
return render_template('youtube.html', videos=videos)
|
||||
return render_template('youtube.html', videos=videos, followCount=followCount)
|
||||
|
||||
@app.route('/ytfollowing', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def ytfollowing():
|
||||
form = EmptyForm()
|
||||
channelList = current_user.youtube_following_list()
|
||||
channelCount = len(channelList)
|
||||
|
||||
return render_template('ytfollowing.html', form=form, channelList=channelList, channelCount=channelCount)
|
||||
|
||||
@app.route('/ytsearch', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@ -254,35 +248,41 @@ def ytsearch():
|
||||
def ytfollow(channelId):
|
||||
form = EmptyForm()
|
||||
if form.validate_on_submit():
|
||||
channel = invidiousFollow.query.filter_by(channelId=channelId).first()
|
||||
if requests.get('https://{instance}/feed/channel/{cid}'.format(instance=invidiousInstance, cid=channelId)).status_code == 200:
|
||||
if channel is None:
|
||||
follow = invidiousFollow()
|
||||
follow.channelId = channelId
|
||||
follow.followers.append(current_user)
|
||||
try:
|
||||
db.session.add(follow)
|
||||
db.session.commit()
|
||||
except:
|
||||
flash("Something went wrong. Try again!")
|
||||
return redirect(request.referrer)
|
||||
flash('You are following {}!'.format(channelId))
|
||||
else:
|
||||
flash("Something went wrong... try again")
|
||||
return redirect(request.referrer)
|
||||
else:
|
||||
return redirect(request.referrer)
|
||||
r = followYoutubeChannel(channelId)
|
||||
return redirect(request.referrer)
|
||||
|
||||
def followYoutubeChannel(channelId):
|
||||
channel = youtubeFollow.query.filter_by(channelId=channelId).first()
|
||||
channelData = YoutubeSearch.channelInfo(channelId, False)
|
||||
try:
|
||||
follow = youtubeFollow()
|
||||
follow.channelId = channelId
|
||||
follow.channelName = channelData[0]['name']
|
||||
follow.followers.append(current_user)
|
||||
db.session.add(follow)
|
||||
db.session.commit()
|
||||
flash("{} followed!".format(channelData[0]['name']))
|
||||
return True
|
||||
except:
|
||||
flash("Couldn't follow {}. Maybe you are already following!".format(channelData[0]['name']))
|
||||
return False
|
||||
|
||||
@app.route('/ytunfollow/<channelId>', methods=['POST'])
|
||||
@login_required
|
||||
def ytunfollow(channelId):
|
||||
form = EmptyForm()
|
||||
channel = invidiousFollow.query.filter_by(channelId=channelId).first()
|
||||
if form.validate_on_submit():
|
||||
r = unfollowYoutubeChannel(channelId)
|
||||
return redirect(request.referrer)
|
||||
|
||||
def unfollowYoutubeChannel(channelId):
|
||||
try:
|
||||
channel = youtubeFollow.query.filter_by(channelId=channelId).first()
|
||||
db.session.delete(channel)
|
||||
db.session.commit()
|
||||
flash("User unfollowed!")
|
||||
flash("{} unfollowed!".format(channel.channelName))
|
||||
except:
|
||||
print("Exception")
|
||||
flash("There was an error unfollowing the user. Try again.")
|
||||
return redirect(request.referrer)
|
||||
|
||||
@ -295,7 +295,7 @@ def channel(id):
|
||||
data = feedparser.parse(data.content)
|
||||
|
||||
channelData = YoutubeSearch.channelInfo(id)
|
||||
return render_template('channel.html', form=form, btform=button_form, channel=channelData[0], videos=channelData[1])
|
||||
return render_template('channel.html', form=form, btform=button_form, channel=channelData[1], videos=channelData[0])
|
||||
|
||||
@app.route('/watch', methods=['GET'])
|
||||
@login_required
|
||||
@ -397,6 +397,34 @@ def exportData():
|
||||
except:
|
||||
return False
|
||||
|
||||
@app.route('/importdata', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def importdata():
|
||||
if request.method == 'POST':
|
||||
print("Post request recieved")
|
||||
# check if the post request has the file part
|
||||
if 'file' not in request.files:
|
||||
flash('No file part')
|
||||
return redirect(request.url)
|
||||
file = request.files['file']
|
||||
# if user does not select file, browser also
|
||||
# submit an empty part without filename
|
||||
if file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(request.url)
|
||||
if file and allowed_file(file.filename):
|
||||
filename = secure_filename(file.filename)
|
||||
data = json.load(file)
|
||||
for acc in data['twitter']:
|
||||
if twFollow(acc['username']):
|
||||
print("{} followed!".format(acc['username']))
|
||||
else:
|
||||
print("Something went wrong!")
|
||||
return redirect(request.referrer)
|
||||
|
||||
def allowed_file(filename):
|
||||
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if current_user.is_authenticated:
|
||||
|
@ -17,7 +17,7 @@
|
||||
<div class="item">
|
||||
<div class="right floated content">
|
||||
<p>
|
||||
<form action="{{ url_for('unfollowList', username=acc.username) }}" method="post">
|
||||
<form action="{{ url_for('unfollow', username=acc.username) }}" method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.submit(value='Unfollow') }}
|
||||
</form>
|
||||
|
@ -23,6 +23,17 @@
|
||||
<div class="description">Export data into JSON file</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="large upload middle aligned icon"></i>
|
||||
<div class="content">
|
||||
<form action = "{{ url_for('importdata') }}" method = "POST" enctype = "multipart/form-data">
|
||||
<input type = "file" name = "file"/>
|
||||
<input type = "submit"/>
|
||||
</form>
|
||||
<div class="description">Import data from JSON file</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<i class="large moon middle aligned icon"></i>
|
||||
<div class="content">
|
||||
|
@ -18,7 +18,7 @@
|
||||
<div class="ui one column centered grid">
|
||||
{% if user == current_user %}
|
||||
<p><a>This is your profile</a></p>
|
||||
{% elif not current_user.is_following(user) %}
|
||||
{% elif not current_user.is_following_tw(user.username) %}
|
||||
<p>
|
||||
<form action="{{ url_for('follow', username=user.username) }}" method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
|
@ -1,15 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<br>
|
||||
<br>
|
||||
{% if videos %}
|
||||
<div class="ui centered cards">
|
||||
{% for video in videos %}
|
||||
{% include '_video_item.html' %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% include '_empty_feed.html' %}
|
||||
{% endif %}
|
||||
<div style="padding: 1.3em;" class="ui one column centered grid">
|
||||
<a href="{{ url_for('ytfollowing') }}"><div class="ui small red statistic">
|
||||
<div class="value">
|
||||
{{ followCount }}
|
||||
</div>
|
||||
<div class="label">
|
||||
Following
|
||||
</div>
|
||||
</div></a>
|
||||
</div>
|
||||
{% if videos %}
|
||||
<div class="ui centered cards">
|
||||
{% for video in videos %}
|
||||
{% include '_video_item.html' %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% include '_empty_feed.html' %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
36
app/templates/ytfollowing.html
Normal file
36
app/templates/ytfollowing.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div style="padding: 1.3em;" class="ui one column centered grid">
|
||||
<a href="{{ url_for('ytfollowing') }}"><div class="ui red statistic">
|
||||
<div class="value">
|
||||
{{ channelCount }}
|
||||
</div>
|
||||
<div class="label">
|
||||
Following
|
||||
</div>
|
||||
</div></a>
|
||||
</div>
|
||||
|
||||
<div style="margin: 0em;" class="ui one column centered grid">
|
||||
<div class="ui middle aligned divided list">
|
||||
{% for channel in channelList %}
|
||||
<div class="item">
|
||||
<div class="right floated content">
|
||||
<p>
|
||||
<form action="{{ url_for('ytunfollow', channelId=channel.channelId) }}" method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.submit(value='Unfollow') }}
|
||||
</form>
|
||||
</p>
|
||||
</div>
|
||||
<img class="ui avatar image" src="{{ url_for('static',filename='img/avatars/')}}{{range(1, 12) | random}}.png">
|
||||
<div class="content">
|
||||
<a href="{{ url_for('channel', id=channel.channelId)}}">{{channel.channelName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
Reference in New Issue
Block a user