diff --git a/web_server/blueprints/streams.py b/web_server/blueprints/streams.py index f0a6c62..6e6b1c3 100644 --- a/web_server/blueprints/streams.py +++ b/web_server/blueprints/streams.py @@ -1,12 +1,10 @@ -from flask import Blueprint, session, jsonify, g, request, redirect, abort, send_from_directory +from flask import Blueprint, session, jsonify, request, redirect, abort from utils.stream_utils import * +from utils.recommendation_utils import * from utils.user_utils import get_user_id from blueprints.utils import login_required -from utils.recommendation_utils import * -from utils.utils import most_popular_category from database.database import Database from datetime import datetime - from celery_tasks import update_thumbnail stream_bp = Blueprint("stream", __name__) @@ -14,6 +12,8 @@ stream_bp = Blueprint("stream", __name__) # Constants THUMBNAIL_GENERATION_INTERVAL = 180 + +## Stream Routes @stream_bp.route('/streams/popular/') def get_popular_streams(no_streams) -> list[dict]: """ @@ -31,7 +31,6 @@ def get_popular_streams(no_streams) -> list[dict]: streams = get_highest_view_streams(no_streams) return jsonify(streams) - @login_required @stream_bp.route('/streams/recommended') def get_recommended_streams() -> list[dict]: @@ -46,6 +45,8 @@ def get_recommended_streams() -> list[dict]: streams = get_streams_based_on_category(category) return streams + +## Category Routes @stream_bp.route('/categories/popular/') def get_popular_categories(no_categories) -> list[dict]: """ @@ -71,51 +72,6 @@ def get_recommended_categories() -> list | list[dict]: categories = get_user_category_recommendations(user_id) return jsonify(categories) - -@stream_bp.route('/user/') -def get_user_data(username): - """ - Returns a given user's data - """ - user_id = get_user_id(username) - if not user_id: - abort(404) - data = get_streamer_data(user_id) - return jsonify(data) - - -@stream_bp.route('/user//status') -def get_user_live_status(streamer_username): - """ - Returns a streamer's status, if they are live or not and their most recent stream (their current stream if live) - """ - user_id = get_user_id(streamer_username) - - is_live = True if get_streamer_live_status(user_id)['is_live'] else False - - most_recent_vod = get_latest_vod(user_id) - - if not most_recent_vod: - most_recent_vod = None - else: - most_recent_vod = most_recent_vod['vod_id'] - - return jsonify({ - "is_live": is_live, - "most_recent_stream": most_recent_vod - }) - - -@stream_bp.route('/user//vods') -def get_vods(streamer_username): - """ - Returns a JSON of all the vods of a streamer - """ - user_id = get_user_id(streamer_username) - vods = get_user_vods(user_id) - return jsonify(vods) - - @login_required @stream_bp.route('/categories/following') def get_following_categories_streams(): @@ -127,16 +83,40 @@ def get_following_categories_streams(): return jsonify(streams) -@login_required -@stream_bp.route('/users/following') -def get_followed_streamers_(): +## User Routes +@stream_bp.route('/user//status') +def get_user_live_status(username): """ - Queries DB to get a list of followed streamers + Returns a streamer's status, if they are live or not and their most recent stream (their current stream if live) """ - user_id = session.get('user_id') + user_id = get_user_id(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) + + # If there is no most recent vod, set it to None + if not most_recent_vod: + most_recent_vod = None + else: + most_recent_vod = most_recent_vod['vod_id'] + + return jsonify({ + "is_live": is_live, + "most_recent_stream": most_recent_vod + }) + + +## VOD Routes +@stream_bp.route('/vods/') +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) + return jsonify(vods) - live_following_streams = get_followed_streamers(user_id) - return live_following_streams ## RTMP Server Routes @stream_bp.route("/publish_stream", methods=["POST"]) @@ -184,6 +164,4 @@ 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 diff --git a/web_server/blueprints/user.py b/web_server/blueprints/user.py index 456b893..c51c1d2 100644 --- a/web_server/blueprints/user.py +++ b/web_server/blueprints/user.py @@ -1,11 +1,23 @@ -from flask import Blueprint, jsonify, session -from utils.user_utils import is_subscribed, is_following, subscription_expiration, verify_token, reset_password, get_user_id, unfollow +from flask import Blueprint, jsonify, session, abort +from utils.user_utils import * from blueprints.utils import login_required user_bp = Blueprint("user", __name__) +@user_bp.route('/user/') +def get_user_data_(username): + """ + Returns a given user's data + """ + user_id = get_user_id(username) + if not user_id: + abort(404) + data = get_user_data(user_id) + return jsonify(data) + +## Subscription Routes @login_required -@user_bp.route('/is_subscribed/') +@user_bp.route('/user/subscription/') def user_subscribed(subscribed_id: int): """ Checks to see if user is subscribed to another user @@ -15,17 +27,30 @@ def user_subscribed(subscribed_id: int): return jsonify({"subscribed": True}) return jsonify({"subscribed": False}) -@user_bp.route('/is_following//') -def user_following(user_id: int, subscribed_id: int): +@login_required +@user_bp.route('/user/subscription//expiration') +def user_subscription_expiration(subscribed_id: int): + """ + Returns remaining time until subscription expiration + """ + + user_id = session.get("user_id") + remaining_time = subscription_expiration(user_id, subscribed_id) + + return jsonify({"remaining_time": remaining_time}) + +## Follow Routes +@user_bp.route('/user//follows/') +def user_following(user_id: int, followed_id: int): """ Checks to see if user is following a streamer """ - if is_following(user_id, subscribed_id): + if is_following(user_id, followed_id): return jsonify({"following": True}) return jsonify({"following": False}) @login_required -@user_bp.route('/follow/') +@user_bp.route('/user/follow/') def follow(username): """ Follows a user @@ -34,9 +59,8 @@ def follow(username): following_id = get_user_id(username) follow(user_id, following_id) - @login_required -@user_bp.route('/unfollow/') +@user_bp.route('/user/unfollow/') def user_unfollow(followed_username): """ Unfollows a user @@ -46,18 +70,18 @@ def user_unfollow(followed_username): unfollow(user_id, followed_id) @login_required -@user_bp.route('/subscription_remaining/') -def user_subscription_expiration(streamer_id: int): +@user_bp.route('/user/following') +def get_followed_streamers_(): """ - Returns remaining time until subscription expiration + Queries DB to get a list of followed streamers """ + user_id = session.get('user_id') - user_id = session.get("user_id") - remaining_time = subscription_expiration(user_id, streamer_id) + live_following_streams = get_followed_streamers(user_id) + return live_following_streams - return jsonify({"remaining_time": remaining_time}) - -@user_bp.route('/get_login_status') +## Login Routes +@user_bp.route('/user/login_status') def get_login_status(): """ Returns whether the user is logged in or not @@ -65,7 +89,7 @@ def get_login_status(): username = session.get("username") return jsonify({'status': username is not None, 'username': username}) -@user_bp.route('/forgot_password/', methods=['POST']) +@user_bp.route('/user/forgot_password/', methods=['POST']) def user_forgot_password(email): """ Will send link to email to reset password by looking at the user_id within session to see whos password should be reset @@ -74,7 +98,7 @@ def user_forgot_password(email): return -@user_bp.route('/reset_password//') +@user_bp.route('/user/reset_password//') def user_reset_password(token, new_password): """ Given token and new password resets the users password diff --git a/web_server/database/app.db b/web_server/database/app.db index 718f8d2..2c84a01 100644 Binary files a/web_server/database/app.db and b/web_server/database/app.db differ diff --git a/web_server/database/testing_data.sql b/web_server/database/testing_data.sql index aaa788d..3272ea1 100644 --- a/web_server/database/testing_data.sql +++ b/web_server/database/testing_data.sql @@ -131,4 +131,8 @@ INSERT INTO follows (user_id, followed_id, since) VALUES INSERT INTO followed_categories (user_id, category_id) VALUES (7, 1), (7, 2), -(7, 3); \ No newline at end of file +(7, 3); + +INSERT INTO subscribes (user_id, subscribed_id, since, expires) VALUES +(7, 1, '2024-08-30', '2025-02-28'), +(7, 2, '2024-08-30', '2025-02-15'); \ No newline at end of file diff --git a/web_server/database/users.sql b/web_server/database/users.sql index 25b699b..b72733a 100644 --- a/web_server/database/users.sql +++ b/web_server/database/users.sql @@ -46,7 +46,7 @@ CREATE TABLE subscribes subscribed_id INTEGER NOT NULL, since DATETIME NOT NULL, expires DATETIME NOT NULL, - PRIMARY KEY (user_id), + PRIMARY KEY (user_id, subscribed_id), FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, FOREIGN KEY (subscribed_id) REFERENCES users(user_id) ON DELETE CASCADE ); diff --git a/web_server/utils/stream_utils.py b/web_server/utils/stream_utils.py index e512fb1..fd547d5 100644 --- a/web_server/utils/stream_utils.py +++ b/web_server/utils/stream_utils.py @@ -34,18 +34,6 @@ def get_followed_live_streams(user_id: int) -> Optional[List[dict]]: """, (user_id,)) return live_streams -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_vod(vod_id: int) -> dict: """ Returns data of a streamers vod @@ -71,17 +59,6 @@ def get_user_vods(user_id: int): return vods -def get_streamer_data(user_id: int) -> Optional[dict]: - """ - Returns information about the streamer - """ - with Database() as db: - data = db.fetchone(""" - SELECT username, bio, num_followers, is_partnered FROM users - WHERE user_id = ?; - """, (user_id,)) - return data - def generate_thumbnail(user_id: int) -> None: """ Generates the thumbnail of a stream diff --git a/web_server/utils/user_utils.py b/web_server/utils/user_utils.py index c4bb5fc..bb1c4f0 100644 --- a/web_server/utils/user_utils.py +++ b/web_server/utils/user_utils.py @@ -1,5 +1,5 @@ from database.database import Database -from typing import Optional +from typing import Optional, List from datetime import datetime from itsdangerous import URLSafeTimedSerializer from os import getenv @@ -45,19 +45,22 @@ def is_user_partner(user_id: int) -> bool: """, (user_id,)) return bool(data) -def is_subscribed(user_id: int, streamer_id: int) -> bool: +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 1 + SELECT * FROM subscribes WHERE user_id = ? - AND streamer_id = ? - AND expires > ? - """, (user_id, streamer_id, datetime.now())) - return bool(result) + 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: """ @@ -108,7 +111,7 @@ def subscription_expiration(user_id: int, subscribed_id: int) -> int: with Database() as db: data = db.fetchone(""" SELECT expires - FROM subscriptions + FROM subscribes WHERE user_id = ? AND subscribed_id = ? AND expires > ? @@ -149,4 +152,27 @@ def get_email(user_id: int) -> Optional[str]: WHERE user_id = ? """, (user_id,)) - return email["email"] if email else None \ No newline at end of file + 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_data(user_id: int) -> Optional[dict]: + """ + Returns username, bio, number of followers, and if user is partnered from user_id + """ + with Database() as db: + data = db.fetchone(""" + SELECT username, bio, num_followers, is_partnered FROM users + WHERE user_id = ?; + """, (user_id,)) + return data \ No newline at end of file