From 43edc52c25b6a03c83c8380b0da98a324ba44696 Mon Sep 17 00:00:00 2001 From: white <122345776@umail.ucc.ie> Date: Fri, 7 Feb 2025 17:18:46 +0000 Subject: [PATCH] UPDATE: migrated one time util methods to blueprints --- web_server/blueprints/authentication.py | 2 +- web_server/blueprints/chat.py | 2 +- web_server/blueprints/oauth.py | 2 +- web_server/blueprints/streams.py | 89 ++++++++++++++-- web_server/blueprints/user.py | 129 +++++++++++++++++++++++- web_server/utils/stream_utils.py | 124 +---------------------- web_server/utils/user_utils.py | 120 +--------------------- 7 files changed, 210 insertions(+), 258 deletions(-) diff --git a/web_server/blueprints/authentication.py b/web_server/blueprints/authentication.py index 36e4a80..99f58fd 100644 --- a/web_server/blueprints/authentication.py +++ b/web_server/blueprints/authentication.py @@ -4,7 +4,7 @@ from flask_cors import cross_origin from database.database import Database from blueprints.utils import login_required, sanitizer from blueprints.email import send_email -from utils.user_utils import get_user_id +from blueprints.user import get_user_id from secrets import token_hex auth_bp = Blueprint("auth", __name__) diff --git a/web_server/blueprints/chat.py b/web_server/blueprints/chat.py index a5a903f..ac5bfff 100644 --- a/web_server/blueprints/chat.py +++ b/web_server/blueprints/chat.py @@ -3,7 +3,7 @@ from database.database import Database from .socket import socketio from flask_socketio import emit, join_room, leave_room from datetime import datetime -from utils.user_utils import get_user_id +from blueprints.user import get_user_id chat_bp = Blueprint("chat", __name__) diff --git a/web_server/blueprints/oauth.py b/web_server/blueprints/oauth.py index 98971e6..f45e49b 100644 --- a/web_server/blueprints/oauth.py +++ b/web_server/blueprints/oauth.py @@ -1,6 +1,6 @@ from authlib.integrations.flask_client import OAuth, OAuthError from flask import Blueprint, url_for, jsonify, session -from utils.user_utils import get_session_info_email +from blueprints.user import get_session_info_email oauth_bp = Blueprint("oauth", __name__) def init_oauth(app): diff --git a/web_server/blueprints/streams.py b/web_server/blueprints/streams.py index 4f6479d..82d3758 100644 --- a/web_server/blueprints/streams.py +++ b/web_server/blueprints/streams.py @@ -1,10 +1,11 @@ from flask import Blueprint, session, jsonify, request, redirect from utils.stream_utils import * -from utils.user_utils import get_user_id +from blueprints.user import get_user_id from blueprints.utils import login_required from database.database import Database from datetime import datetime from celery_tasks import update_thumbnail +from typing import List, Optional stream_bp = Blueprint("stream", __name__) @@ -45,9 +46,15 @@ def get_popular_streams_by_category(category_name) -> list[dict]: Returns a list of streams live now with the highest viewers in a given category """ - category_id = get_category_id(category_name) - with Database() as db: + + data = db.fetchone(""" + SELECT category_id + FROM categories + WHERE category_name = ?; + """, (category_name,)) + category_id = data["category_id"] if data else None + streams = db.fetchall(""" SELECT u.user_id, title, username, num_viewers, c.category_name FROM streams s @@ -97,8 +104,29 @@ def get_stream_data(streamer_id): """ Returns a streamer's current stream data """ - - return jsonify(get_current_stream_data(streamer_id)) + with Database() as db: + most_recent_stream = db.fetchone(""" + SELECT s.user_id, u.username, s.title, s.start_time, s.num_viewers, c.category_name + FROM streams AS s + JOIN categories AS c ON s.category_id = c.category_id + JOIN users AS u ON s.user_id = u.user_id + WHERE u.user_id = ? + """, (streamer_id,)) + + return jsonify(most_recent_stream) + +def get_stream_tags(user_id: int) -> Optional[List[str]]: + """ + Given a stream return tags associated with the user's 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 user_id = ?; + """, (user_id,)) + return tags ## Category Routes @@ -176,7 +204,9 @@ def get_user_live_status(username): # Check if streamer is live and get their most recent vod is_live = True if get_streamer_live_status(user_id)['is_live'] else False - most_recent_vod = get_latest_vod(user_id) + + with Database() as db: + most_recent_vod = db.fetchone("""SELECT * FROM vods WHERE user_id = ? ORDER BY vod_id DESC LIMIT 1;""", (user_id,)) # If there is no most recent vod, set it to None if not most_recent_vod: @@ -197,9 +227,25 @@ def get_vods(username): Returns a JSON of all the vods of a streamer """ user_id = get_user_id(username) - vods = get_user_vods(user_id) + + with Database() as db: + vods = db.fetchall("""SELECT * FROM vods WHERE user_id = ?;""", (user_id,)) + return jsonify(vods) +def get_vod_tags(vod_id: int): + """ + Given a vod return tags associated with the vod + """ + with Database() as db: + tags = db.fetchall(""" + SELECT tag_name + FROM tags + JOIN vod_tags ON tags.tag_id = vod_tags.tag_id + WHERE vod_id = ?; + """, (vod_id,)) + return tags + ## RTMP Server Routes @stream_bp.route("/publish_stream", methods=["POST"]) @@ -247,4 +293,31 @@ def end_stream(): # Remove stream from database db.execute("""DELETE FROM streams WHERE user_id = ?""", (user_info["user_id"],)) - return "Stream ended", 200 \ No newline at end of file + return "Stream ended", 200 + +def transfer_stream_to_vod(user_id: int): + """ + Deletes stream from stream table and moves it to VoD table + TODO: Add functionaliy to save stream permanently + """ + + with Database() as db: + stream = db.fetchone(""" + SELECT * FROM streams WHERE user_id = ?; + """, (user_id,)) + + if not stream: + return None + + ## TODO: calculate length in seconds, currently using temp value + + db.execute(""" + INSERT INTO vods (user_id, title, datetime, category_id, length, views) + VALUES (?, ?, ?, ?, ?, ?); + """, (stream["user_id"], stream["title"], stream["datetime"], stream["category_id"], 10, stream["num_viewers"])) + + db.execute(""" + DELETE FROM streams WHERE user_id = ?; + """, (user_id,)) + + return True \ No newline at end of file diff --git a/web_server/blueprints/user.py b/web_server/blueprints/user.py index e8354bc..ff1bf02 100644 --- a/web_server/blueprints/user.py +++ b/web_server/blueprints/user.py @@ -20,7 +20,93 @@ def get_user_data(username: str): data = get_user(user_id) return jsonify(data) +def get_user_id(username: str) -> Optional[int]: + """ + Returns user_id associated with given username + """ + with Database() as db: + data = db.fetchone(""" + SELECT user_id + FROM users + WHERE username = ? + """, (username,)) + return data['user_id'] if data else None + +def get_username(user_id: str) -> Optional[str]: + """ + Returns username associated with given user_id + """ + with Database() as db: + data = db.fetchone(""" + SELECT username + FROM user + WHERE user_id = ? + """, (user_id,)) + return data['username'] if data else None + +def get_email(user_id: int) -> Optional[str]: + with Database() as db: + email = db.fetchone(""" + SELECT email + FROM users + WHERE user_id = ? + """, (user_id,)) + + return email["email"] if email else None + +def get_session_info_email(email: str) -> dict: + """ + Returns username and user_id given email + """ + with Database() as db: + session_info = db.fetchone(""" + SELECT user_id, username + FROM user + WHERE email = ? + """, (email,)) + return session_info + +def is_user_partner(user_id: int) -> bool: + """ + Returns True if user is a partner, else False + """ + with Database() as db: + data = db.fetchone(""" + SELECT is_partnered + FROM users + WHERE user_id = ? + """, (user_id,)) + return bool(data) + +def get_user(user_id: int) -> Optional[dict]: + """ + Returns information about a user from user_id + """ + with Database() as db: + data = db.fetchone(""" + SELECT user_id, username, bio, num_followers, is_partnered, is_live FROM users + WHERE user_id = ?; + """, (user_id,)) + return data + ## Subscription Routes +def is_subscribed(user_id: int, subscribed_to_id: int) -> bool: + """ + Returns True if user is subscribed to a streamer, else False + """ + with Database() as db: + result = db.fetchone(""" + SELECT * + FROM subscribes + WHERE user_id = ? + AND subscribed_id = ? + AND expires > ?; + """, (user_id, subscribed_to_id, datetime.now())) + print(result) + if result: + return True + return False + @login_required @user_bp.route('/user/subscription/') def user_subscribed(subscribed_id: int): @@ -38,10 +124,21 @@ def user_subscription_expiration(subscribed_id: int): """ Returns remaining time until subscription expiration """ + with Database() as db: + data = db.fetchone(""" + SELECT expires + FROM subscribes + WHERE user_id = ? + AND subscribed_id = ? + AND expires > ? + """, (session.get("user_id"), subscribed_id, datetime.now())) - user_id = session.get("user_id") - remaining_time = subscription_expiration(user_id, subscribed_id) - + if data: + expiration_date = data["expires"] + remaining_time = (parser.parse(expiration_date) - datetime.now()).seconds + else: + remaining_time = 0 + return jsonify({"remaining_time": remaining_time}) ## Follow Routes @@ -82,8 +179,30 @@ def get_followed_streamers(): """ user_id = session.get('user_id') - live_following_streams = get_followed_streamers(user_id) - return live_following_streams + with Database() as db: + followed_streamers = db.fetchall(""" + SELECT user_id, username + FROM users + WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?); + """, (user_id,)) + + return followed_streamers + +def get_followed_live_streams(user_id: int) -> Optional[List[dict]]: + """ + Searches for streamers who the user followed which are currently live + Returns a list of live streams with the streamer's user id, stream title, and number of viewers + """ + with Database() as db: + live_streams = db.fetchall(""" + SELECT users.user_id, streams.title, streams.num_viewers, users.username + FROM streams JOIN users + ON streams.user_id = users.user_id + WHERE users.user_id IN + (SELECT followed_id FROM follows WHERE user_id = ?) + AND users.is_live = 1; + """, (user_id,)) + return live_streams ## Login Routes @user_bp.route('/user/login_status') diff --git a/web_server/utils/stream_utils.py b/web_server/utils/stream_utils.py index 7328294..732ab30 100644 --- a/web_server/utils/stream_utils.py +++ b/web_server/utils/stream_utils.py @@ -1,9 +1,5 @@ from database.database import Database -from typing import Optional -import sqlite3, os, subprocess -from time import sleep -from typing import Optional, List -from datetime import datetime +import os, subprocess def get_streamer_live_status(user_id: int): """ @@ -18,72 +14,6 @@ def get_streamer_live_status(user_id: int): return is_live -def get_followed_live_streams(user_id: int) -> Optional[List[dict]]: - """ - Searches for streamers who the user followed which are currently live - Returns a list of live streams with the streamer's user id, stream title, and number of viewers - """ - with Database() as db: - live_streams = db.fetchall(""" - SELECT users.user_id, streams.title, streams.num_viewers, users.username - FROM streams JOIN users - ON streams.user_id = users.user_id - WHERE users.user_id IN - (SELECT followed_id FROM follows WHERE user_id = ?) - AND users.is_live = 1; - """, (user_id,)) - return live_streams - -def get_current_stream_data(user_id: int) -> Optional[dict]: - """ - Returns data of the most recent stream by a streamer - """ - with Database() as db: - most_recent_stream = db.fetchone(""" - SELECT s.user_id, u.username, s.title, s.start_time, s.num_viewers, c.category_name - FROM streams AS s - JOIN categories AS c ON s.category_id = c.category_id - JOIN users AS u ON s.user_id = u.user_id - WHERE u.user_id = ? - """, (user_id,)) - return most_recent_stream - -def get_category_id(category_name: str) -> Optional[int]: - """ - Returns the category_id given a category name - """ - with Database() as db: - data = db.fetchone(""" - SELECT category_id - FROM categories - WHERE category_name = ?; - """, (category_name,)) - return data['category_id'] if data else None - -def get_vod(vod_id: int) -> dict: - """ - Returns data of a streamers vod - """ - with Database() as db: - vod = db.fetchone("""SELECT * FROM vods WHERE vod_id = ?;""", (vod_id,)) - return vod - -def get_latest_vod(user_id: int): - """ - Returns data of the most recent stream by a streamer - """ - with Database() as db: - latest_vod = db.fetchone("""SELECT * FROM vods WHERE user_id = ? ORDER BY vod_id DESC LIMIT 1;""", (user_id,)) - return latest_vod - -def get_user_vods(user_id: int): - """ - Returns data of all vods by a streamer - """ - with Database() as db: - vods = db.fetchall("""SELECT * FROM vods WHERE user_id = ?;""", (user_id,)) - return vods - def generate_thumbnail(user_id: int) -> None: """ @@ -112,55 +42,3 @@ def generate_thumbnail(user_id: int) -> None: subprocess.run(thumbnail_command) -def get_stream_tags(user_id: int) -> Optional[List[str]]: - """ - Given a stream return tags associated with the user's 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 user_id = ?; - """, (user_id,)) - return tags - -def get_vod_tags(vod_id: int): - """ - Given a vod return tags associated with the vod - """ - with Database() as db: - tags = db.fetchall(""" - SELECT tag_name - FROM tags - JOIN vod_tags ON tags.tag_id = vod_tags.tag_id - WHERE vod_id = ?; - """, (vod_id,)) - return tags - -def transfer_stream_to_vod(user_id: int): - """ - Deletes stream from stream table and moves it to VoD table - TODO: Add functionaliy to save stream permanently - """ - - with Database() as db: - stream = db.fetchone(""" - SELECT * FROM streams WHERE user_id = ?; - """, (user_id,)) - - if not stream: - return None - - ## TODO: calculate length in seconds, currently using temp value - - db.execute(""" - INSERT INTO vods (user_id, title, datetime, category_id, length, views) - VALUES (?, ?, ?, ?, ?, ?); - """, (stream["user_id"], stream["title"], stream["datetime"], stream["category_id"], 10, stream["num_viewers"])) - - db.execute(""" - DELETE FROM streams WHERE user_id = ?; - """, (user_id,)) - - return True \ No newline at end of file diff --git a/web_server/utils/user_utils.py b/web_server/utils/user_utils.py index ca5d5eb..30a3841 100644 --- a/web_server/utils/user_utils.py +++ b/web_server/utils/user_utils.py @@ -3,78 +3,13 @@ from typing import Optional, List from datetime import datetime from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired from os import getenv -from werkzeug.security import generate_password_hash, check_password_hash +from werkzeug.security import generate_password_hash from dateutil import parser from dotenv import load_dotenv load_dotenv() serializer = URLSafeTimedSerializer(getenv("AUTH_SECRET_KEY")) -def get_user_id(username: str) -> Optional[int]: - """ - Returns user_id associated with given username - """ - with Database() as db: - data = db.fetchone(""" - SELECT user_id - FROM users - WHERE username = ? - """, (username,)) - return data['user_id'] if data else None - -def get_username(user_id: str) -> Optional[str]: - """ - Returns username associated with given user_id - """ - with Database() as db: - data = db.fetchone(""" - SELECT username - FROM user - WHERE user_id = ? - """, (user_id,)) - return data['username'] if data else None - -def get_session_info_email(email: str) -> dict: - """ - Returns username and user_id given email - """ - with Database as db: - session_info = db.fetchone(""" - SELECT user_id, username - FROM user - WHERE email = ? - """, (email,)) - return session_info - -def is_user_partner(user_id: int) -> bool: - """ - Returns True if user is a partner, else False - """ - with Database() as db: - data = db.fetchone(""" - SELECT is_partnered - FROM users - WHERE user_id = ? - """, (user_id,)) - return bool(data) - -def is_subscribed(user_id: int, subscribed_to_id: int) -> bool: - """ - Returns True if user is subscribed to a streamer, else False - """ - with Database() as db: - result = db.fetchone(""" - SELECT * - FROM subscribes - WHERE user_id = ? - AND subscribed_id = ? - AND expires > ?; - """, (user_id, subscribed_to_id, datetime.now())) - print(result) - if result: - return True - return False - def is_following(user_id: int, followed_id: int) -> bool: """ Returns where a user is following another @@ -116,27 +51,6 @@ def unfollow(user_id: int, followed_id: int): """, (user_id, followed_id)) return {"success": True} - -def subscription_expiration(user_id: int, subscribed_id: int) -> int: - """ - Returns the amount of time left until user subscription to a streamer ends - """ - with Database() as db: - data = db.fetchone(""" - SELECT expires - FROM subscribes - WHERE user_id = ? - AND subscribed_id = ? - AND expires > ? - """, (user_id, subscribed_id, datetime.now())) - - if data: - expiration_date = data["expires"] - remaining_time = (parser.parse(expiration_date) - datetime.now()).seconds - return remaining_time - - return 0 - def generate_token(email, salt_value) -> str: """ Creates a token for password reset @@ -174,35 +88,3 @@ def reset_password(new_password: str, email: str) -> bool: return True -def get_email(user_id: int) -> Optional[str]: - with Database() as db: - email = db.fetchone(""" - SELECT email - FROM users - WHERE user_id = ? - """, (user_id,)) - - return email["email"] if email else None - -def get_followed_streamers(user_id: int) -> Optional[List[dict]]: - """ - Returns a list of streamers who the user follows - """ - with Database() as db: - followed_streamers = db.fetchall(""" - SELECT user_id, username - FROM users - WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?); - """, (user_id,)) - return followed_streamers - -def get_user(user_id: int) -> Optional[dict]: - """ - Returns information about a user from user_id - """ - with Database() as db: - data = db.fetchone(""" - SELECT user_id, username, bio, num_followers, is_partnered, is_live FROM users - WHERE user_id = ?; - """, (user_id,)) - return data \ No newline at end of file