UPDATE: Refactored backend routes to improve readability

This commit is contained in:
2025-02-05 21:04:05 +00:00
parent 4c79c80f3d
commit a8af24e256
10 changed files with 228 additions and 193 deletions

View File

@@ -13,7 +13,6 @@ services:
- app_network - app_network
volumes: volumes:
- stream_data:/stream_data - stream_data:/stream_data
web_server: web_server:
build: build:
context: ./web_server context: ./web_server

View File

@@ -21,4 +21,4 @@ COPY . .
ENV FLASK_APP=blueprints.__init__ ENV FLASK_APP=blueprints.__init__
ENV FLASK_DEBUG=True ENV FLASK_DEBUG=True
CMD ["python", "-c", "from blueprints.socket import socketio; from blueprints.__init__ import create_app; app = create_app(); socketio.run(app, host='0.0.0.0', port=5000, debug=True)"] CMD ["python", "-c", "from blueprints.socket import socketio; from blueprints.__init__ import create_app; app = create_app(); app.debug = True; socketio.run(app, host='0.0.0.0', port=5000, debug=True)"]

View File

@@ -28,6 +28,7 @@ def create_app():
app.config["SECRET_KEY"] = getenv("FLASK_SECRET_KEY") app.config["SECRET_KEY"] = getenv("FLASK_SECRET_KEY")
app.config["SESSION_PERMANENT"] = False app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem" app.config["SESSION_TYPE"] = "filesystem"
app.config["PROPAGATE_EXCEPTIONS"] = True
app.config.from_mapping( app.config.from_mapping(
CELERY=dict( CELERY=dict(

View File

@@ -1,23 +1,8 @@
from flask import Blueprint, session, jsonify, g, request, redirect, abort, send_from_directory from flask import Blueprint, session, jsonify, g, request, redirect, abort, send_from_directory
from utils.stream_utils import ( from utils.stream_utils import *
streamer_live_status,
streamer_most_recent_stream,
user_stream,
followed_live_streams,
followed_streamers,
stream_tags,
streamer_data
)
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.recommendation_utils import *
default_recommendations,
recommendations_based_on_category,
user_recommendation_category,
followed_categories_recommendations,
category_recommendations,
user_category_recommendations
)
from utils.utils import most_popular_category 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
@@ -29,137 +14,128 @@ stream_bp = Blueprint("stream", __name__)
# Constants # Constants
THUMBNAIL_GENERATION_INTERVAL = 180 THUMBNAIL_GENERATION_INTERVAL = 180
@stream_bp.route('/get_streams') @stream_bp.route('/streams/popular/<int:no_streams>')
def get_sample_streams() -> list[dict]: def get_popular_streams(no_streams) -> list[dict]:
""" """
Returns a list of streams live now with the highest viewers Returns a list of streams live now with the highest viewers
""" """
# shows default recommended streams for non-logged in users based on highest viewers # Limit the number of streams to MAX_STREAMS
streams = default_recommendations() MAX_STREAMS = 100
for stream in streams: if no_streams < 1:
stream['tags'] = stream_tags(stream["stream_id"]) return jsonify([])
elif no_streams > MAX_STREAMS:
no_streams = MAX_STREAMS
# Get the highest viewed streams
streams = get_highest_view_streams(no_streams)
return jsonify(streams) return jsonify(streams)
@login_required @login_required
@stream_bp.route('/get_recommended_streams') @stream_bp.route('/streams/recommended')
def get_recommended_streams() -> list[dict]: def get_recommended_streams() -> list[dict]:
""" """
Queries DB to get a list of recommended streams using an algorithm Queries DB to get a list of recommended streams using an algorithm
""" """
user_id = session.get("username") user_id = session.get("user_id")
category = user_recommendation_category(user_id)
streams = recommendations_based_on_category(category)
for stream in streams:
stream['tags'] = stream_tags(stream["stream_id"])
return jsonify(streams)
@stream_bp.route('/get_categories') # Get the user's most popular categories
def get_categories() -> list[dict]: category = get_user_preferred_category(user_id)
""" streams = get_streams_based_on_category(category)
Returns a list of top 5 most popular categories return streams
"""
category_data = category_recommendations() @stream_bp.route('/categories/popular/<int:no_categories>')
def get_popular_categories(no_categories) -> list[dict]:
"""
Returns a list of most popular categories
"""
# Limit the number of categories to 100
if no_categories < 1:
return jsonify([])
elif no_categories > 100:
no_categories = 100
category_data = get_highest_view_categories(no_categories)
return jsonify(category_data) return jsonify(category_data)
@login_required @login_required
@stream_bp.route('/get_recommended_categories') @stream_bp.route('/categories/recommended')
def get_recommended_categories() -> list | list[dict]: def get_recommended_categories() -> list | list[dict]:
""" """
Queries DB to get a list of recommended categories for the user Queries DB to get a list of recommended categories for the user
""" """
user_id = session.get("user_id") user_id = session.get("user_id")
categories = user_category_recommendations(user_id) categories = get_user_category_recommendations(user_id)
return categories return jsonify(categories)
@stream_bp.route('/get_streamer_data/<string:streamer_username>') @stream_bp.route('/user/<string:username>')
def get_streamer_data(streamer_username): def get_user_data(username):
""" """
Returns a given streamer's data Returns a given user's data
""" """
streamer_id = get_user_id(streamer_username) user_id = get_user_id(username)
if not streamer_id: if not user_id:
abort(404) abort(404)
data = streamer_data(streamer_id) data = get_streamer_data(user_id)
return data return jsonify(data)
@stream_bp.route('/streamer/<string:streamer_username>/status') @stream_bp.route('/user/<string:streamer_username>/status')
def get_streamer_status(streamer_username): 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) 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) user_id = get_user_id(streamer_username)
if not user_id: is_live = True if get_streamer_live_status(user_id)['is_live'] else False
abort(404)
is_live = True if streamer_live_status(user_id)['isLive'] else False most_recent_vod = get_latest_vod(user_id)
most_recent_stream = streamer_most_recent_stream(user_id)['stream_id']
if not most_recent_stream: if not most_recent_vod:
most_recent_stream = None most_recent_vod = None
else:
most_recent_vod = most_recent_vod['vod_id']
return jsonify({ return jsonify({
"is_live": is_live, "is_live": is_live,
"most_recent_stream": most_recent_stream "most_recent_stream": most_recent_vod
}) })
@stream_bp.route('/get_stream_data/<string:streamer_username>') @stream_bp.route('/user/<string:streamer_username>/vods')
def get_stream(streamer_username): def get_vods(streamer_username):
""" """
Returns a streamer's most recent stream data Returns a JSON of all the vods of a streamer
""" """
user_id = get_user_id(streamer_username) user_id = get_user_id(streamer_username)
if not user_id: vods = get_user_vods(user_id)
abort(404) return jsonify(vods)
return jsonify(streamer_most_recent_stream(user_id))
@stream_bp.route('/get_stream_data/<string:streamer_username>/<int:stream_id>')
def get_specific_stream(streamer_username, stream_id):
"""
Returns a streamer's stream data given stream_id
"""
user_id = get_user_id(streamer_username)
stream = user_stream(user_id, stream_id)
if stream:
return jsonify(stream)
return jsonify({'error': 'Stream not found'}), 404
@login_required @login_required
@stream_bp.route('/get_followed_category_streams') @stream_bp.route('/categories/following')
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(session.get('user_id'))
for stream in streams:
stream['tags'] = stream_tags(stream["stream_id"])
return jsonify(streams) return jsonify(streams)
@login_required @login_required
@stream_bp.route('/get_followed_streamers') @stream_bp.route('/users/following')
def get_followed_streamers(): def get_followed_streamers_():
""" """
Queries DB to get a list of followed streamers Queries DB to get a list of followed streamers
""" """
username = session.get('username') user_id = session.get('user_id')
user_id = get_user_id(username)
live_following_streams = followed_streamers(user_id) live_following_streams = get_followed_streamers(user_id)
return live_following_streams return live_following_streams
## RTMP Server Routes ## RTMP Server Routes
@@ -197,12 +173,17 @@ def end_stream():
Ends a stream Ends a stream
""" """
db = Database() db = Database()
# get stream key
user_info = db.fetchone("""SELECT user_id FROM users WHERE stream_key = ?""", (request.form.get("name"),)) user_info = db.fetchone("""SELECT user_id FROM users WHERE stream_key = ?""", (request.form.get("name"),))
stream_info = db.fetchone("""SELECT stream_id FROM streams WHERE user_id = ?""", (user_info["user_id"],))
if not user_info: if not user_info:
return "Unauthorized", 403 return "Unauthorized", 403
# Set stream to not live # Remove stream from database
db.execute("""UPDATE streams SET isLive = 0 WHERE user_id = ? AND isLive = 1""", (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,5 +1,5 @@
from celery import Celery, shared_task, Task from celery import Celery, shared_task, Task
from utils.stream_utils import generate_thumbnail, streamer_live_status from utils.stream_utils import generate_thumbnail, get_streamer_live_status
from time import sleep from time import sleep
def celery_init_app(app) -> Celery: def celery_init_app(app) -> Celery:
@@ -22,7 +22,7 @@ def update_thumbnail(user_id, sleep_time=180) -> None:
ffmpeg_wait_time = 5 ffmpeg_wait_time = 5
# check if user is streaming # check if user is streaming
while streamer_live_status(user_id)['isLive']: while get_streamer_live_status(user_id)['isLive']:
sleep(ffmpeg_wait_time) sleep(ffmpeg_wait_time)
generate_thumbnail(user_id) generate_thumbnail(user_id)
sleep(sleep_time - ffmpeg_wait_time) sleep(sleep_time - ffmpeg_wait_time)

Binary file not shown.

View File

@@ -8,9 +8,18 @@ CREATE TABLE tags
DROP TABLE IF EXISTS stream_tags; DROP TABLE IF EXISTS stream_tags;
CREATE TABLE stream_tags CREATE TABLE stream_tags
( (
stream_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL, tag_id INTEGER NOT NULL,
FOREIGN KEY (stream_id) REFERENCES streams(stream_id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES streams(user_id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(tag_id) ON DELETE CASCADE
);
DROP TABLE IF EXISTS vod_tags;
CREATE TABLE vod_tags
(
vod_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (vod_id) REFERENCES vods(vod_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
); );
@@ -38,14 +47,13 @@ CREATE TABLE categories
DROP TABLE IF EXISTS streams; DROP TABLE IF EXISTS streams;
CREATE TABLE streams CREATE TABLE streams
( (
stream_id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL, title TEXT NOT NULL,
datetime DATETIME NOT NULL, datetime DATETIME NOT NULL,
num_viewers INTEGER NOT NULL DEFAULT 0, num_viewers INTEGER NOT NULL DEFAULT 0,
category_id INTEGER NOT NULL, category_id INTEGER NOT NULL,
FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE CASCADE, FOREIGN KEY (category_id) REFERENCES categories(category_id),
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(user_id)
); );
DROP TABLE IF EXISTS vods; DROP TABLE IF EXISTS vods;
@@ -55,7 +63,7 @@ CREATE TABLE vods
user_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
title TEXT NOT NULL, title TEXT NOT NULL,
datetime DATETIME NOT NULL, datetime DATETIME NOT NULL,
category_id INTEGER, category_id INTEGER NOT NULL,
length INTEGER NOT NULL, length INTEGER NOT NULL,
views INTEGER NOT NULL DEFAULT 0, views INTEGER NOT NULL DEFAULT 0,

View File

@@ -1,10 +1,10 @@
-- Sample Data for users -- Sample Data for users
INSERT INTO users (username, password, email, num_followers, stream_key, is_partnered, bio, current_stream_title, current_selected_category_id) VALUES INSERT INTO users (username, password, email, num_followers, stream_key, is_partnered, bio, is_live, current_stream_title, current_selected_category_id) VALUES
('GamerDude', 'password123', 'gamerdude@example.com', 500, '1234', 0, 'Streaming my gaming adventures!', 'Epic Gaming Session', 1), ('GamerDude', 'password123', 'gamerdude@example.com', 500, '1234', 0, 'Streaming my gaming adventures!', 1, 'Epic Gaming Session', 1),
('MusicLover', 'music4life', 'musiclover@example.com', 1200, '2345', 0, 'I share my favorite tunes.', 'Live Music Jam', 2), ('MusicLover', 'music4life', 'musiclover@example.com', 1200, '2345', 0, 'I share my favorite tunes.', 1, 'Live Music Jam', 2),
('ArtFan', 'artistic123', 'artfan@example.com', 300, '3456', 0, 'Exploring the world of art.', 'Sketching Live', 3), ('ArtFan', 'artistic123', 'artfan@example.com', 300, '3456', 0, 'Exploring the world of art.', 1, 'Sketching Live', 3),
('EduGuru', 'learn123', 'eduguru@example.com', 800, '4567', 0, 'Teaching everything I know.', 'Math Made Easy', 4), ('EduGuru', 'learn123', 'eduguru@example.com', 800, '4567', 0, 'Teaching everything I know.', 1, 'Math Made Easy', 4),
('SportsStar', 'sports123', 'sportsstar@example.com', 2000, '5678', 0, 'Join me for live sports updates!', 'Sports Highlights', 5); ('SportsStar', 'sports123', 'sportsstar@example.com', 2000, '5678', 0, 'Join me for live sports updates!', 0, 'Sports Highlights', 5);
INSERT INTO users (username, password, email, num_followers, stream_key, is_partnered, bio) VALUES INSERT INTO users (username, password, email, num_followers, stream_key, is_partnered, bio) VALUES
('GamerDude2', 'password123', 'gamerdude3@gmail.com', 3200, '7890', 0, 'Streaming my gaming adventures!'), ('GamerDude2', 'password123', 'gamerdude3@gmail.com', 3200, '7890', 0, 'Streaming my gaming adventures!'),
@@ -14,10 +14,12 @@ INSERT INTO users (username, password, email, num_followers, stream_key, is_part
INSERT INTO follows (user_id, followed_id, since) VALUES INSERT INTO follows (user_id, followed_id, since) VALUES
(1, 2, '2024-12-01'), (1, 2, '2024-12-01'),
(2, 3, '2024-11-15'), (2, 3, '2024-11-15'),
(1, 3, '2024-11-15'),
(3, 4, '2024-10-20'), (3, 4, '2024-10-20'),
(4, 5, '2024-09-12'), (4, 5, '2024-09-12'),
(5, 1, '2024-08-30'); (5, 1, '2024-08-30');
-- Sample Data for user_preferences -- Sample Data for user_preferences
INSERT INTO user_preferences (user_id, category_id, favourability) VALUES INSERT INTO user_preferences (user_id, category_id, favourability) VALUES
(1, 1, 10), (1, 1, 10),
@@ -55,8 +57,7 @@ INSERT INTO streams (user_id, title, datetime, num_viewers, category_id) VALUES
(1, 'Epic Gaming Session', '2025-01-25 18:00:00', 150, 1), (1, 'Epic Gaming Session', '2025-01-25 18:00:00', 150, 1),
(2, 'Live Music Jam', '2025-01-25 20:00:00', 350, 2), (2, 'Live Music Jam', '2025-01-25 20:00:00', 350, 2),
(3, 'Sketching Live', '2025-01-24 15:00:00', 80, 3), (3, 'Sketching Live', '2025-01-24 15:00:00', 80, 3),
(4, 'Math Made Easy', '2025-01-23 10:00:00', 400, 4), (4, 'Math Made Easy', '2025-01-23 10:00:00', 400, 4);
(5, 'Sports Highlights', '2025-01-25 12:00:00', 500, 5);
-- Sample Data for vods -- Sample Data for vods
INSERT INTO vods (user_id, title, datetime, category_id, length, views) VALUES INSERT INTO vods (user_id, title, datetime, category_id, length, views) VALUES
@@ -73,7 +74,14 @@ INSERT INTO tags(tag_name) VALUES
('LGBTQIA+'); ('LGBTQIA+');
-- Sample Data for stream_tags -- Sample Data for stream_tags
INSERT INTO stream_tags (stream_id, tag_id) VALUES INSERT INTO stream_tags (user_id, tag_id) VALUES
(1, 3),
(1, 1),
(2, 1),
(2, 2);
-- Sample Data for vod_tags
INSERT INTO vod_tags (vod_id, tag_id) VALUES
(1, 3), (1, 3),
(1, 1), (1, 1),
(2, 1), (2, 1),
@@ -99,19 +107,28 @@ SELECT * FROM stream_tags;
-- To see all tables in the database -- To see all tables in the database
SELECT name FROM sqlite_master WHERE type='table'; SELECT name FROM sqlite_master WHERE type='table';
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 = 1)
AND users.is_live = 1;
SELECT s.stream_id, s.title, u.username, s.num_viewers, c.category_name SELECT categories.category_id, categories.category_name, SUM(streams.num_viewers) AS total_viewers
FROM streams AS s FROM streams
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 GROUP BY categories.category_name
JOIN followed_categories AS f ON s.category_id = c.category_id ORDER BY SUM(streams.num_viewers) DESC
WHERE f.user_id = 1 LIMIT 10;
ORDER BY s.num_viewers DESC
LIMIT 25;
SELECT username, message, time_sent INSERT INTO follows (user_id, followed_id, since) VALUES
FROM chat (7, 1, '2024-08-30'),
JOIN users ON chat.chatter_id = users.user_id (7, 2, '2024-08-30'),
WHERE stream_id = 1 (7, 3, '2024-08-30'),
ORDER BY time_sent DESC (7, 4, '2024-08-30'),
LIMIT 50; (7, 5, '2024-08-30');
INSERT INTO followed_categories (user_id, category_id) VALUES
(7, 1),
(7, 2),
(7, 3);

View File

@@ -2,7 +2,7 @@ from database.database import Database
from typing import Optional, List from typing import Optional, List
def user_recommendation_category(user_id: int) -> Optional[int]: def get_user_preferred_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
""" """
@@ -23,9 +23,9 @@ def followed_categories_recommendations(user_id: int) -> Optional[List[dict]]:
""" """
with Database() as db: with Database() as db:
streams = db.fetchall(""" streams = db.fetchall("""
SELECT stream_id, title, username, num_viewers, category_name SELECT u.user_id, title, u.username, num_viewers, category_name
FROM streams FROM streams
JOIN users ON users.user_id = streams.user_id JOIN users u ON streams.user_id = u.user_id
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
WHERE categories.category_id IN (SELECT category_id FROM followed_categories WHERE user_id = ?) WHERE categories.category_id IN (SELECT category_id FROM followed_categories WHERE user_id = ?)
ORDER BY num_viewers DESC ORDER BY num_viewers DESC
@@ -34,15 +34,15 @@ def followed_categories_recommendations(user_id: int) -> Optional[List[dict]]:
return streams return streams
def recommendations_based_on_category(category_id: int) -> Optional[List[dict]]: def get_streams_based_on_category(category_id: int) -> Optional[List[dict]]:
""" """
Queries stream database to get top 25 most viewed streams based on given category Queries stream database to get top 25 most viewed streams based on given category
""" """
with Database() as db: with Database() as db:
streams = db.fetchall(""" streams = db.fetchall("""
SELECT streams.stream_id, title, username, num_viewers, category_name SELECT u.user_id, title, username, num_viewers, category_name
FROM streams FROM streams
JOIN users ON users.user_id = streams.user_id JOIN users u ON streams.user_id = u.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
@@ -51,41 +51,37 @@ def recommendations_based_on_category(category_id: int) -> Optional[List[dict]]:
return streams return streams
def default_recommendations() -> Optional[List[dict]]: def get_highest_view_streams(no_streams: int) -> Optional[List[dict]]:
""" """
Return a list of 25 recommended live streams by number of viewers Return a list of live streams by number of viewers
""" """
with Database() as db: with Database() as db:
data = db.fetchall(""" data = db.fetchall("""
SELECT streams.stream_id, title, username, num_viewers, category_name SELECT u.user_id, username, title, num_viewers, category_name
FROM streams FROM streams
JOIN users ON users.user_id = streams.user_id JOIN users u ON streams.user_id = u.user_id
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
WHERE isLive = 1
ORDER BY num_viewers DESC ORDER BY num_viewers DESC
LIMIT 25; LIMIT ?;
""") """, (no_streams,))
return data return data
def get_highest_view_categories(no_categories: int) -> Optional[List[dict]]:
def category_recommendations() -> Optional[List[dict]]:
""" """
Returns a list of the top 5 most popular live categories Returns a list of top 5 most popular categories
""" """
with Database() as db: with Database() as db:
categories = db.fetchall(""" categories = db.fetchall("""
SELECT categories.category_id, categories.category_name SELECT categories.category_id, categories.category_name, SUM(streams.num_viewers) AS total_viewers
FROM streams FROM streams
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
WHERE streams.isLive = 1
GROUP BY categories.category_name GROUP BY categories.category_name
ORDER BY SUM(streams.num_viewers) DESC ORDER BY SUM(streams.num_viewers) DESC
LIMIT 5; LIMIT ?;
""") """, (no_categories,))
return categories return categories
def get_user_category_recommendations(user_id: int) -> Optional[List[dict]]:
def user_category_recommendations(user_id: int) -> Optional[List[dict]]:
""" """
Queries user_preferences database to find users top 5 favourite streaming category and returns the category Queries user_preferences database to find users top 5 favourite streaming category and returns the category
""" """

View File

@@ -3,37 +3,38 @@ 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 from typing import Optional, List
from datetime import datetime
def streamer_live_status(user_id: int) -> dict: def get_streamer_live_status(user_id: int):
""" """
Returns boolean on whether the given streamer is live Returns boolean on whether the given streamer is live
""" """
with Database() as db: with Database() as db:
is_live = db.fetchone(""" is_live = db.fetchone("""
SELECT isLive SELECT is_live
FROM streams FROM users
WHERE user_id = ? WHERE user_id = ?;
ORDER BY stream_id DESC
LIMIT 1;
""", (user_id,)) """, (user_id,))
return is_live return is_live
def followed_live_streams(user_id: int) -> Optional[List[dict]]: def get_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
Returns a list of live streams with the streamer's user id, stream title, and number of viewers
""" """
with Database() as db: with Database() as db:
live_streams = db.fetchall(""" live_streams = db.fetchall("""
SELECT user_id, stream_id, title, num_viewers SELECT users.user_id, streams.title, streams.num_viewers, users.username
FROM streams FROM streams JOIN users
WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?) ON streams.user_id = users.user_id
AND stream_id = (SELECT MAX(stream_id) FROM streams WHERE user_id = streams.user_id) WHERE users.user_id IN
AND isLive = 1; (SELECT followed_id FROM follows WHERE user_id = ?)
AND users.is_live = 1;
""", (user_id,)) """, (user_id,))
return live_streams return live_streams
def followed_streamers(user_id: int) -> Optional[List[dict]]: def get_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
""" """
@@ -45,54 +46,48 @@ def followed_streamers(user_id: int) -> Optional[List[dict]]:
""", (user_id,)) """, (user_id,))
return followed_streamers return followed_streamers
def user_stream(user_id: int, stream_id: int) -> dict: def get_vod(vod_id: int) -> dict:
""" """
Returns data of a streamers selected stream Returns data of a streamers vod
""" """
with Database() as db: with Database() as db:
stream = db.fetchone(""" vod = db.fetchone("""SELECT * FROM vods WHERE vod_id = ?;""", (vod_id,))
SELECT u.username, s.user_id, s.title, s.start_time, s.num_viewers, c.category_name return vod
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 = ?
AND s.stream_id = ?
""", (user_id, stream_id))
return stream
def streamer_most_recent_stream(user_id: int) -> Optional[dict]: def get_latest_vod(user_id: int):
""" """
Returns data of the most recent stream by a streamer Returns data of the most recent stream by a streamer
""" """
with Database() as db: with Database() as db:
most_recent_stream = db.fetchone(""" latest_vod = db.fetchone("""SELECT * FROM vods WHERE user_id = ? ORDER BY vod_id DESC LIMIT 1;""", (user_id,))
SELECT s.stream_id, u.username, s.user_id, s.title, s.start_time, s.num_viewers, c.category_name return latest_vod
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 = ?
AND s.stream_id = (SELECT MAX(stream_id) FROM streams WHERE user_id = ?)
""", (user_id, user_id))
return most_recent_stream
def streamer_data(streamer_id: int) -> Optional[dict]: 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 get_streamer_data(user_id: int) -> Optional[dict]:
""" """
Returns information about the streamer Returns information about the streamer
""" """
with Database() as db: with Database() as db:
data = db.fetchone(""" data = db.fetchone("""
SELECT username, bio, num_followers, is_partnered FROM users SELECT username, bio, num_followers, is_partnered FROM users
WHERE user_id = ? WHERE user_id = ?;
""", (streamer_id,)) """, (user_id,))
return data return data
def generate_thumbnail(user_id: int) -> None: def generate_thumbnail(user_id: int) -> None:
""" """
Returns the thumbnail of a stream Generates the thumbnail of a stream
""" """
db = Database() with Database() as db:
username = db.fetchone("""SELECT * FROM users WHERE user_id = ?""", (user_id,)) username = db.fetchone("""SELECT * FROM users WHERE user_id = ?""", (user_id,))
db.close_connection()
if not username: if not username:
return None return None
@@ -100,8 +95,6 @@ def generate_thumbnail(user_id: int) -> None:
if not os.path.exists(f"stream_data/thumbnails/"): if not os.path.exists(f"stream_data/thumbnails/"):
os.makedirs(f"stream_data/thumbnails/") os.makedirs(f"stream_data/thumbnails/")
subprocess.Popen(["ls", "-lR"])
thumbnail_command = [ thumbnail_command = [
"ffmpeg", "ffmpeg",
"-y", "-y",
@@ -115,16 +108,56 @@ def generate_thumbnail(user_id: int) -> None:
] ]
subprocess.run(thumbnail_command) subprocess.run(thumbnail_command)
def stream_tags(stream_id: int) -> Optional[List[str]]:
def get_stream_tags(user_id: int) -> Optional[List[str]]:
""" """
Given a stream return tags associated with the stream Given a stream return tags associated with the user's stream
""" """
with Database() as db: with Database() as db:
tags = db.fetchall(""" tags = db.fetchall("""
SELECT tag_name SELECT tag_name
FROM tags FROM tags
JOIN stream_tags ON tags.tag_id = stream_tags.tag_id JOIN stream_tags ON tags.tag_id = stream_tags.tag_id
WHERE stream_id = ? WHERE user_id = ?;
""", (stream_id,)) """, (user_id,))
tags = [tag['tag_name'] for tag in tags] if tags else None
return tags 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