This commit is contained in:
EvanLin3141
2025-02-06 01:35:29 +00:00
7 changed files with 121 additions and 112 deletions

View File

@@ -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.stream_utils import *
from utils.recommendation_utils import *
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
from utils.recommendation_utils import *
from utils.utils import most_popular_category
from database.database import Database from database.database import Database
from datetime import datetime from datetime import datetime
from celery_tasks import update_thumbnail from celery_tasks import update_thumbnail
stream_bp = Blueprint("stream", __name__) stream_bp = Blueprint("stream", __name__)
@@ -14,6 +12,8 @@ stream_bp = Blueprint("stream", __name__)
# Constants # Constants
THUMBNAIL_GENERATION_INTERVAL = 180 THUMBNAIL_GENERATION_INTERVAL = 180
## Stream Routes
@stream_bp.route('/streams/popular/<int:no_streams>') @stream_bp.route('/streams/popular/<int:no_streams>')
def get_popular_streams(no_streams) -> list[dict]: 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) streams = get_highest_view_streams(no_streams)
return jsonify(streams) return jsonify(streams)
@login_required @login_required
@stream_bp.route('/streams/recommended') @stream_bp.route('/streams/recommended')
def get_recommended_streams() -> list[dict]: def get_recommended_streams() -> list[dict]:
@@ -46,6 +45,8 @@ def get_recommended_streams() -> list[dict]:
streams = get_streams_based_on_category(category) streams = get_streams_based_on_category(category)
return streams return streams
## Category Routes
@stream_bp.route('/categories/popular/<int:no_categories>') @stream_bp.route('/categories/popular/<int:no_categories>')
def get_popular_categories(no_categories) -> list[dict]: 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) categories = get_user_category_recommendations(user_id)
return jsonify(categories) return jsonify(categories)
@stream_bp.route('/user/<string:username>')
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/<string:streamer_username>/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/<string:streamer_username>/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 @login_required
@stream_bp.route('/categories/following') @stream_bp.route('/categories/following')
def get_following_categories_streams(): def get_following_categories_streams():
@@ -127,16 +83,40 @@ def get_following_categories_streams():
return jsonify(streams) return jsonify(streams)
@login_required ## User Routes
@stream_bp.route('/users/following') @stream_bp.route('/user/<string:username>/status')
def get_followed_streamers_(): 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/<string:username>')
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 ## RTMP Server Routes
@stream_bp.route("/publish_stream", methods=["POST"]) @stream_bp.route("/publish_stream", methods=["POST"])
@@ -184,6 +164,4 @@ def end_stream():
# Remove stream from database # Remove stream from database
db.execute("""DELETE FROM streams WHERE user_id = ?""", (user_info["user_id"],)) db.execute("""DELETE FROM streams WHERE user_id = ?""", (user_info["user_id"],))
#
return "Stream ended", 200 return "Stream ended", 200

View File

@@ -1,11 +1,23 @@
from flask import Blueprint, jsonify, session from flask import Blueprint, jsonify, session, abort
from utils.user_utils import is_subscribed, is_following, subscription_expiration, verify_token, reset_password, get_user_id, unfollow from utils.user_utils import *
from blueprints.utils import login_required from blueprints.utils import login_required
user_bp = Blueprint("user", __name__) user_bp = Blueprint("user", __name__)
@user_bp.route('/user/<string:username>')
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 @login_required
@user_bp.route('/is_subscribed/<int:subscribed_id>') @user_bp.route('/user/subscription/<int:subscribed_id>')
def user_subscribed(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
@@ -15,17 +27,30 @@ def user_subscribed(subscribed_id: int):
return jsonify({"subscribed": True}) return jsonify({"subscribed": True})
return jsonify({"subscribed": False}) return jsonify({"subscribed": False})
@user_bp.route('/is_following/<int:user_id>/<int:subscribed_id>') @login_required
def user_following(user_id: int, subscribed_id: int): @user_bp.route('/user/subscription/<int:subscribed_id>/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/<int:user_id>/follows/<int:followed_id>')
def user_following(user_id: int, followed_id: int):
""" """
Checks to see if user is following a streamer 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": True})
return jsonify({"following": False}) return jsonify({"following": False})
@login_required @login_required
@user_bp.route('/follow/<string:username>') @user_bp.route('/user/follow/<string:username>')
def follow(username): def follow(username):
""" """
Follows a user Follows a user
@@ -34,9 +59,8 @@ def follow(username):
following_id = get_user_id(username) following_id = get_user_id(username)
follow(user_id, following_id) follow(user_id, following_id)
@login_required @login_required
@user_bp.route('/unfollow/<string:username>') @user_bp.route('/user/unfollow/<string:username>')
def user_unfollow(followed_username): def user_unfollow(followed_username):
""" """
Unfollows a user Unfollows a user
@@ -46,18 +70,18 @@ def user_unfollow(followed_username):
unfollow(user_id, followed_id) unfollow(user_id, followed_id)
@login_required @login_required
@user_bp.route('/subscription_remaining/<int:streamer_id>') @user_bp.route('/user/following')
def user_subscription_expiration(streamer_id: int): 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") live_following_streams = get_followed_streamers(user_id)
remaining_time = subscription_expiration(user_id, streamer_id) return live_following_streams
return jsonify({"remaining_time": remaining_time}) ## Login Routes
@user_bp.route('/user/login_status')
@user_bp.route('/get_login_status')
def get_login_status(): def get_login_status():
""" """
Returns whether the user is logged in or not Returns whether the user is logged in or not
@@ -65,7 +89,7 @@ def get_login_status():
username = session.get("username") username = session.get("username")
return jsonify({'status': username is not None, 'username': username}) return jsonify({'status': username is not None, 'username': username})
@user_bp.route('/forgot_password/<string:email>', methods=['POST']) @user_bp.route('/user/forgot_password/<string:email>', methods=['POST'])
def user_forgot_password(email): 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 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 return
@user_bp.route('/reset_password/<string:token>/<string:new_password>') @user_bp.route('/user/reset_password/<string:token>/<string:new_password>')
def user_reset_password(token, new_password): def user_reset_password(token, new_password):
""" """
Given token and new password resets the users password Given token and new password resets the users password

Binary file not shown.

View File

@@ -132,3 +132,7 @@ INSERT INTO followed_categories (user_id, category_id) VALUES
(7, 1), (7, 1),
(7, 2), (7, 2),
(7, 3); (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');

View File

@@ -46,7 +46,7 @@ CREATE TABLE subscribes
subscribed_id INTEGER NOT NULL, subscribed_id INTEGER NOT NULL,
since DATETIME NOT NULL, since DATETIME NOT NULL,
expires 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 (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
FOREIGN KEY (subscribed_id) REFERENCES users(user_id) ON DELETE CASCADE FOREIGN KEY (subscribed_id) REFERENCES users(user_id) ON DELETE CASCADE
); );

View File

@@ -34,18 +34,6 @@ def get_followed_live_streams(user_id: int) -> Optional[List[dict]]:
""", (user_id,)) """, (user_id,))
return live_streams 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: def get_vod(vod_id: int) -> dict:
""" """
Returns data of a streamers vod Returns data of a streamers vod
@@ -71,17 +59,6 @@ def get_user_vods(user_id: int):
return vods 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: def generate_thumbnail(user_id: int) -> None:
""" """
Generates the thumbnail of a stream Generates the thumbnail of a stream

View File

@@ -1,5 +1,5 @@
from database.database import Database from database.database import Database
from typing import Optional from typing import Optional, List
from datetime import datetime from datetime import datetime
from itsdangerous import URLSafeTimedSerializer from itsdangerous import URLSafeTimedSerializer
from os import getenv from os import getenv
@@ -45,19 +45,22 @@ def is_user_partner(user_id: int) -> bool:
""", (user_id,)) """, (user_id,))
return bool(data) 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 Returns True if user is subscribed to a streamer, else False
""" """
with Database() as db: with Database() as db:
result = db.fetchone(""" result = db.fetchone("""
SELECT 1 SELECT *
FROM subscribes FROM subscribes
WHERE user_id = ? WHERE user_id = ?
AND streamer_id = ? AND subscribed_id = ?
AND expires > ? AND expires > ?;
""", (user_id, streamer_id, datetime.now())) """, (user_id, subscribed_to_id, datetime.now()))
return bool(result) print(result)
if result:
return True
return False
def is_following(user_id: int, followed_id: int) -> bool: 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: with Database() as db:
data = db.fetchone(""" data = db.fetchone("""
SELECT expires SELECT expires
FROM subscriptions FROM subscribes
WHERE user_id = ? WHERE user_id = ?
AND subscribed_id = ? AND subscribed_id = ?
AND expires > ? AND expires > ?
@@ -150,3 +153,26 @@ def get_email(user_id: int) -> Optional[str]:
""", (user_id,)) """, (user_id,))
return email["email"] if email else None 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