FEAT UPDATE: Updated Utils to use new database. Changed route arguments. Added unfollow feature

This commit is contained in:
JustIceO7
2025-01-31 02:00:31 +00:00
parent be53d4072e
commit 908870f751
9 changed files with 151 additions and 115 deletions

View File

@@ -34,7 +34,7 @@ export function StreamsProvider({ children }: { children: React.ReactNode }) {
const { isLoggedIn } = useAuth(); const { isLoggedIn } = useAuth();
const fetch_url = isLoggedIn const fetch_url = isLoggedIn
? ["/api/get_recommended_streams", "/api/get_followed_categories"] ? ["/api/get_recommended_streams", "/api/get_followed_category_streams"]
: ["/api/get_streams", "/api/get_categories"]; : ["/api/get_streams", "/api/get_categories"];
useEffect(() => { useEffect(() => {

View File

@@ -4,7 +4,8 @@ from utils.stream_utils import (
streamer_most_recent_stream, streamer_most_recent_stream,
user_stream, user_stream,
followed_live_streams, followed_live_streams,
followed_streamers followed_streamers,
stream_tags
) )
from utils.user_utils import get_user_id from utils.user_utils import get_user_id
from blueprints.utils import login_required from blueprints.utils import login_required
@@ -34,7 +35,8 @@ def get_sample_streams() -> list[dict]:
# shows default recommended streams for non-logged in users based on highest viewers # shows default recommended streams for non-logged in users based on highest viewers
streams = default_recommendations() streams = default_recommendations()
for stream in streams:
stream['tags'] = stream_tags(stream["stream_id"])
return jsonify(streams) return jsonify(streams)
@@ -48,6 +50,8 @@ def get_recommended_streams() -> list[dict]:
user_id = session.get("username") user_id = session.get("username")
category = user_recommendation_category(user_id) category = user_recommendation_category(user_id)
streams = recommendations_based_on_category(category) streams = recommendations_based_on_category(category)
for stream in streams:
stream['tags'] = stream_tags(stream["stream_id"])
return jsonify(streams) return jsonify(streams)
@stream_bp.route('/get_categories') @stream_bp.route('/get_categories')
@@ -120,12 +124,16 @@ def get_stream(streamer_username):
return jsonify(streamer_most_recent_stream(user_id)) return jsonify(streamer_most_recent_stream(user_id))
@login_required @login_required
@stream_bp.route('/get_followed_categories') @stream_bp.route('/get_followed_category_streams')
def get_following_categories_streams(): def get_following_categories_streams():
""" """
Returns popular streams in categories which the user followed Returns popular streams in categories which the user followed
""" """
streams = followed_categories_recommendations(get_user_id(session.get('username'))) streams = followed_categories_recommendations(get_user_id(session.get('username')))
for stream in streams:
stream['tags'] = stream_tags(stream["stream_id"])
return jsonify(streams) return jsonify(streams)

View File

@@ -1,14 +1,16 @@
from flask import Blueprint, jsonify, session from flask import Blueprint, jsonify, session
from utils.user_utils import is_subscribed, is_following, subscription_expiration, verify_token, reset_password from utils.user_utils import is_subscribed, is_following, subscription_expiration, verify_token, reset_password, get_user_id, unfollow
from blueprints.utils import login_required
user_bp = Blueprint("user", __name__) user_bp = Blueprint("user", __name__)
@login_required
@user_bp.route('/is_subscribed/<int:user_id>/<int:subscribed_id>') @user_bp.route('/is_subscribed/<int:subscribed_id>')
def user_subscribed(user_id: int, subscribed_id: int): def user_subscribed(subscribed_id: int):
""" """
Checks to see if user is subscribed to another user Checks to see if user is subscribed to another user
""" """
user_id = session.get("user_id")
if is_subscribed(user_id, subscribed_id): if is_subscribed(user_id, subscribed_id):
return jsonify({"subscribed": True}) return jsonify({"subscribed": True})
return jsonify({"subscribed": False}) return jsonify({"subscribed": False})
@@ -22,12 +24,27 @@ def user_following(user_id: int, subscribed_id: int):
return jsonify({"following": True}) return jsonify({"following": True})
return jsonify({"following": False}) return jsonify({"following": False})
@login_required
@user_bp.route('/unfollow/<int:username>')
def user_unfollow(followed_username):
"""
Unfollows a user
"""
user_id = session.get("user_id")
followed_id = get_user_id(followed_username)
response = unfollow(user_id, followed_id)
@user_bp.route('/subscription_remaining/<int:user_id>/<int:streamer_id>') status = True if response else False
def user_subscription_expiration(user_id: int, streamer_id: int): return jsonify({"status": status})
@login_required
@user_bp.route('/subscription_remaining/<int:streamer_id>')
def user_subscription_expiration(streamer_id: int):
""" """
Returns remaining time until subscription expiration Returns remaining time until subscription expiration
""" """
user_id = session.get("user_id")
remaining_time = subscription_expiration(user_id, streamer_id) remaining_time = subscription_expiration(user_id, streamer_id)
return jsonify({"remaining_time": remaining_time}) return jsonify({"remaining_time": remaining_time})

View File

@@ -10,7 +10,7 @@ CREATE TABLE stream_tags
( (
stream_id INTEGER NOT NULL, stream_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL, tag_id INTEGER NOT NULL,
FOREIGN KEY (stream_id) REFERENCES streams(stream_id), FOREIGN KEY (stream_id) REFERENCES streams(stream_id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(tag_id) ON DELETE CASCADE FOREIGN KEY (tag_id) REFERENCES tags(tag_id) ON DELETE CASCADE
); );

View File

@@ -59,12 +59,3 @@ CREATE TABLE followed_categories
FOREIGN KEY(user_id) REFERENCES users(user_id) ON DELETE CASCADE, FOREIGN KEY(user_id) REFERENCES users(user_id) ON DELETE CASCADE,
FOREIGN KEY(category_id) REFERENCES categories(category_id) ON DELETE CASCADE FOREIGN KEY(category_id) REFERENCES categories(category_id) ON DELETE CASCADE
); );
SELECT s.stream_id, s.title, u.username, s.num_viewers, c.category_name
FROM streams AS s
JOIN users AS u ON u.user_id = s.user_id
JOIN categories AS c ON s.category_id = c.category_id
JOIN followed_categories AS f ON s.category_id = c.category_id
WHERE f.user_id = ?
ORDER BY s.num_viewers DESC
LIMIT 25;

View File

@@ -1,12 +1,12 @@
from database.database import Database from database.database import Database
from typing import Optional, List, Tuple from typing import Optional, List
def user_recommendation_category(user_id: int) -> Optional[int]: def user_recommendation_category(user_id: int) -> Optional[int]:
""" """
Queries user_preferences database to find users favourite streaming category and returns the category Queries user_preferences database to find users favourite streaming category and returns the category
""" """
db = Database() with Database() as db:
data = db.fetchone(""" data = db.fetchone("""
SELECT category_id SELECT category_id
FROM user_preferences FROM user_preferences
WHERE user_id = ? WHERE user_id = ?
@@ -15,66 +15,60 @@ def user_recommendation_category(user_id: int) -> Optional[int]:
""", (user_id,)) """, (user_id,))
return data return data
#TODO Needs to be reworked to get categories instead of streams of categories (below can be done in another function - get_streams_by_category) def followed_categories_recommendations(user_id : int) -> Optional[List[dict]]:
def followed_categories_recommendations(user_id : int):
""" """
Returns top 25 streams given a users category following Returns top 25 streams given a users category following
""" """
db = Database() with Database() as db:
# TODO: Change this to do what the function says streams = db.fetchall("""
categories = db.fetchall(""" SELECT stream_id, title, username, num_viewers, category_name
SELECT s.stream_id, s.title, u.username, s.num_viewers, c.category_name FROM streams
FROM streams AS s JOIN users ON users.user_id = streams.user_id
JOIN users AS u ON u.user_id = s.user_id JOIN categories ON streams.category_id = categories.category_id
JOIN categories AS c ON s.category_id = c.category_id WHERE categories.category_id IN (SELECT category_id FROM followed_categories WHERE user_id = ?)
JOIN followed_categories AS f ON s.category_id = c.category_id ORDER BY num_viewers DESC
WHERE f.user_id = ? LIMIT 25;
ORDER BY s.num_viewers DESC """, (user_id,))
LIMIT 25; return streams
""", (user_id,)) def recommendations_based_on_category(category_id: int) -> Optional[List[dict]]:
return categories
#TODO Needs to be reworked to get categories instead of streams of categories
def recommendations_based_on_category(category_id: int) -> Optional[List[Tuple[int, str, int]]]:
""" """
Queries stream database to get top 25 most viewed streams based on given category and returns Queries stream database to get top 25 most viewed streams based on given category
(user_id, title, username, num_viewers, category_name)
""" """
db = Database() with Database() as db:
data = db.fetchall(""" streams = db.fetchall("""
SELECT streams.category_id, streams.user_id, streams.title, users.username, streams.num_viewers, categories.category_name SELECT streams.stream_id, title, username, num_viewers, category_name
FROM streams FROM streams
JOIN users ON users.user_id = streams.user_id JOIN users ON users.user_id = streams.user_id
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
WHERE categories.category_id = ? WHERE categories.category_id = ?
ORDER BY num_viewers DESC ORDER BY num_viewers DESC
LIMIT 25 LIMIT 25
""", (category_id,)) """, (category_id,))
return data return streams
def default_recommendations():
def default_recommendations() -> Optional[List[dict]]:
""" """
Return a list of 25 recommended live streams by number of viewers Return a list of 25 recommended live streams by number of viewers
(user_id, title, username, num_viewers, category_name)
""" """
db = Database() with Database() as db:
data = db.fetchall(""" data = db.fetchall("""
SELECT stream_id, users.user_id, title, username, num_viewers, category_name SELECT streams.stream_id, title, username, num_viewers, category_name
FROM streams FROM streams
JOIN users ON users.user_id = streams.user_id JOIN users ON users.user_id = streams.user_id
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
ORDER BY num_viewers DESC ORDER BY num_viewers DESC
LIMIT 25; LIMIT 25;
""") """)
return data return data
def category_recommendations(): def category_recommendations() -> Optional[List[dict]]:
""" """
Returns a list of the top 5 most popular categories Returns a list of the top 5 most popular live categories
""" """
db = Database() with Database() as db:
categories = db.fetchall(""" categories = db.fetchall("""
SELECT categories.category_id, categories.category_name SELECT categories.category_id, categories.category_name
FROM streams FROM streams
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
@@ -84,4 +78,3 @@ def category_recommendations():
LIMIT 5; LIMIT 5;
""") """)
return categories return categories

View File

@@ -2,13 +2,14 @@ from database.database import Database
from typing import Optional from typing import Optional
import sqlite3, os, subprocess import sqlite3, os, subprocess
from time import sleep from time import sleep
from typing import Optional, List
def streamer_live_status(user_id: int) -> dict: def streamer_live_status(user_id: int) -> dict:
""" """
Returns boolean on whether the given streamer is live Returns boolean on whether the given streamer is live
""" """
db = Database() with Database() as db:
is_live = db.fetchone(""" is_live = db.fetchone("""
SELECT isLive SELECT isLive
FROM streams FROM streams
WHERE user_id = ? WHERE user_id = ?
@@ -18,12 +19,12 @@ def streamer_live_status(user_id: int) -> dict:
return is_live return is_live
def followed_live_streams(user_id: int) -> list[dict]: def followed_live_streams(user_id: int) -> Optional[List[dict]]:
""" """
Searches for streamers who the user followed which are currently live Searches for streamers who the user followed which are currently live
""" """
db = Database() with Database() as db:
live_streams = db.fetchall(""" live_streams = db.fetchall("""
SELECT user_id, stream_id, title, num_viewers SELECT user_id, stream_id, title, num_viewers
FROM streams FROM streams
WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?) WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?)
@@ -32,12 +33,12 @@ def followed_live_streams(user_id: int) -> list[dict]:
""", (user_id,)) """, (user_id,))
return live_streams return live_streams
def followed_streamers(user_id: int) -> list[dict]: def followed_streamers(user_id: int) -> Optional[List[dict]]:
""" """
Returns a list of streamers who the user follows Returns a list of streamers who the user follows
""" """
db = Database() with Database() as db:
followed_streamers = db.fetchall(""" followed_streamers = db.fetchall("""
SELECT user_id, username SELECT user_id, username
FROM users FROM users
WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?); WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?);
@@ -48,8 +49,8 @@ def streamer_most_recent_stream(user_id: int) -> dict:
""" """
Returns data of the most recent stream by a streamer Returns data of the most recent stream by a streamer
""" """
db = Database() with Database() as db:
most_recent_stream = db.fetchone(""" most_recent_stream = db.fetchone("""
SELECT * FROM streams SELECT * FROM streams
WHERE user_id = ? WHERE user_id = ?
AND stream_id = (SELECT MAX(stream_id) FROM streams WHERE user_id = ?) AND stream_id = (SELECT MAX(stream_id) FROM streams WHERE user_id = ?)
@@ -60,8 +61,8 @@ def user_stream(user_id: int, stream_id: int) -> dict:
""" """
Returns data of a streamers selected stream Returns data of a streamers selected stream
""" """
db = Database() with Database() as db:
stream = db.fetchone(""" stream = db.fetchone("""
SELECT * FROM streams SELECT * FROM streams
WHERE user_id = ? WHERE user_id = ?
AND stream_id = ? AND stream_id = ?
@@ -97,3 +98,16 @@ def generate_thumbnail(user_id: int) -> None:
] ]
subprocess.run(thumbnail_command) subprocess.run(thumbnail_command)
def stream_tags(stream_id: int) -> Optional[List[str]]:
"""
Given a stream return tags associated with the stream
"""
with Database() as db:
tags = db.fetchall("""
SELECT tag_name
FROM tags
JOIN stream_tags ON tags.tag_id = stream_tags.tag_id
WHERE stream_id = ?
""", (stream_id,))
tags = [tag['tag_name'] for tag in tags] if tags else None
return tags

View File

@@ -9,15 +9,15 @@ load_dotenv()
serializer = URLSafeTimedSerializer(getenv("AUTH_SECRET_KEY")) serializer = URLSafeTimedSerializer(getenv("AUTH_SECRET_KEY"))
def get_user_id(username: str) -> int: def get_user_id(username: str) -> Optional[int]:
""" """
Returns user_id associated with given username Returns user_id associated with given username
""" """
db = Database() with Database() as db:
data = db.fetchone(""" data = db.fetchone("""
SELECT user_id SELECT user_id
FROM users FROM users
WHERE username = ?; WHERE username = ?
""", (username,)) """, (username,))
return data['user_id'] if data else None return data['user_id'] if data else None
@@ -25,8 +25,8 @@ def get_username(user_id: str) -> Optional[str]:
""" """
Returns username associated with given user_id Returns username associated with given user_id
""" """
db = Database() with Database() as db:
data = db.fetchone(""" data = db.fetchone("""
SELECT username SELECT username
FROM user FROM user
WHERE user_id = ? WHERE user_id = ?
@@ -37,8 +37,8 @@ def is_user_partner(user_id: int) -> bool:
""" """
Returns True if user is a partner, else False Returns True if user is a partner, else False
""" """
db = Database() with Database() as db:
data = db.fetchone(""" data = db.fetchone("""
SELECT is_partnered SELECT is_partnered
FROM users FROM users
WHERE user_id = ? WHERE user_id = ?
@@ -49,8 +49,8 @@ def is_subscribed(user_id: int, streamer_id: int) -> bool:
""" """
Returns True if user is subscribed to a streamer, else False Returns True if user is subscribed to a streamer, else False
""" """
db = Database() with Database() as db:
result = db.fetchone(""" result = db.fetchone("""
SELECT 1 SELECT 1
FROM subscribes FROM subscribes
WHERE user_id = ? WHERE user_id = ?
@@ -63,8 +63,8 @@ def is_following(user_id: int, followed_id: int) -> bool:
""" """
Returns where a user is following another Returns where a user is following another
""" """
db = Database() with Database() as db:
result = db.fetchone(""" result = db.fetchone("""
SELECT 1 SELECT 1
FROM follows FROM follows
WHERE user_id = ? WHERE user_id = ?
@@ -72,12 +72,25 @@ def is_following(user_id: int, followed_id: int) -> bool:
""", (user_id, followed_id)) """, (user_id, followed_id))
return bool(result) return bool(result)
def unfollow(user_id: int, followed_id: int) -> bool:
"""
Unfollows follow_id user from user_id user
"""
with Database() as db:
db.execute("""
DELETE FROM follows
WHERE user_id = ?
AND followed_id = ?
""", (user_id, followed_id))
return True
return False
def subscription_expiration(user_id: int, subscribed_id: int) -> int: def subscription_expiration(user_id: int, subscribed_id: int) -> int:
""" """
Returns the amount of time left until user subscription to a streamer ends Returns the amount of time left until user subscription to a streamer ends
""" """
db = Database() with Database() as db:
data = db.fetchone(""" data = db.fetchone("""
SELECT expires SELECT expires
FROM subscriptions FROM subscriptions
WHERE user_id = ? WHERE user_id = ?
@@ -92,19 +105,19 @@ def subscription_expiration(user_id: int, subscribed_id: int) -> int:
return 0 return 0
def verify_token(token: str): def verify_token(token: str) -> Optional[str]:
""" """
Given a token verifies token and decodes the token into an email Given a token verifies token and decodes the token into an email
""" """
email = serializer.loads(token, salt='1', max_age=3600) email = serializer.loads(token, salt='1', max_age=3600)
return email if email else False return email if email else False
def reset_password(new_password: str, email: str): def reset_password(new_password: str, email: str) -> bool:
""" """
Given email and new password reset the password for a given user Given email and new password reset the password for a given user
""" """
db = Database() with Database() as db:
db.execute(""" db.execute("""
UPDATE users UPDATE users
SET password = ? SET password = ?
WHERE email = ? WHERE email = ?

View File

@@ -1,29 +1,30 @@
from database.database import Database from database.database import Database
from typing import Optional, List
def categories(): def categories() -> Optional[List[dict]]:
""" """
Returns all possible streaming categories Returns all possible streaming categories
""" """
db = Database() with Database() as db:
all_categories = db.fetchall("SELECT * FROM categories") all_categories = db.fetchall("SELECT * FROM categories")
return all_categories return all_categories
def tags(): def tags() -> Optional[List[dict]]:
""" """
Returns all possible streaming tags Returns all possible streaming tags
""" """
db = Database() with Database() as db:
all_tags = db.fetchall("SELECT * FROM tags") all_tags = db.fetchall("SELECT * FROM tags")
return all_tags return all_tags
def most_popular_category(): def most_popular_category() -> Optional[List[dict]]:
""" """
Returns the most popular category based on live stream viewers Returns the most popular category based on live stream viewers
""" """
db = Database() with Database() as db:
category = db.fetchone(""" category = db.fetchone("""
SELECT categories.category_id, categories.category_name SELECT categories.category_id, categories.category_name
FROM streams FROM streams
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
@@ -34,4 +35,3 @@ def most_popular_category():
""") """)
return category return category