This commit is contained in:
EvanLin3141
2025-03-01 00:44:34 +00:00
12 changed files with 162 additions and 89 deletions

View File

@@ -5,7 +5,7 @@ from utils.user_utils import get_user_id
from blueprints.middleware import login_required
from database.database import Database
from datetime import datetime
from celery_tasks import update_thumbnail, combine_ts_stream
from celery_tasks.streaming import update_thumbnail, combine_ts_stream
from dateutil import parser
from utils.path_manager import PathManager
import json
@@ -198,7 +198,7 @@ def init_stream():
# Create necessary directories
username = user_info["username"]
create_local_directories(username)
create_user_directories(username)
return redirect(f"/stream/{username}")
@@ -216,7 +216,6 @@ def publish_stream():
periodically update thumbnail
"""
try:
data = json.loads(request.form.get("data"))
except json.JSONDecodeError as ex:

View File

@@ -4,6 +4,8 @@ from utils.auth import *
from utils.utils import get_category_id
from blueprints.middleware import login_required
from utils.email import send_email, forgot_password_body, newsletter_conf
from utils.path_manager import PathManager
from celery_tasks.streaming import convert_image_to_png
import redis
from io import BytesIO
@@ -14,6 +16,8 @@ r = redis.from_url(redis_url, decode_responses=True)
user_bp = Blueprint("user", __name__)
path_manager = PathManager()
@user_bp.route('/user/<string:username>')
def user_data(username: str):
"""
@@ -42,13 +46,28 @@ def user_profile_picture_save():
"""
Saves user profile picture
"""
user_id = session.get("user_id")
image = request.files['image']
ext = image.filename.split('.')[-1]
username = session.get("username")
thumbnail_path = path_manager.get_profile_picture_file_path(username)
image.save(f"/web_server/stream_data/{user_id}.{ext}")
# Check if the post request has the file part
if 'image' not in request.files:
return jsonify({"error": "No image found in request"}), 400
# Fetch image, convert to png, and save
image = Image.open(request.files['image'])
image.convert('RGB')
image.save(thumbnail_path, "PNG")
return "Success", 200
return jsonify({"message": "Profile picture saved"})
@user_bp.route('/user/profile_picture/<string:username>')
def user_profile_picture(username: str):
"""
Returns the profile picture of a user
"""
user_id = get_user_id(username)
image = Image.open(f"/web_server/stream_data/{user_id}.jpg")
@login_required
@user_bp.route('/user/same/<string:username>')

View File

@@ -1,10 +1,4 @@
from celery import Celery, shared_task, Task
from utils.stream_utils import generate_thumbnail, get_streamer_live_status
from time import sleep
from os import listdir, remove
from datetime import datetime
from celery_tasks.preferences import user_preferences
import subprocess
def celery_init_app(app) -> Celery:
class FlaskTask(Task):
@@ -23,53 +17,3 @@ def celery_init_app(app) -> Celery:
celery_app.set_default()
app.extensions["celery"] = celery_app
return celery_app
@shared_task
def update_thumbnail(user_id, stream_file, thumbnail_file, sleep_time) -> None:
"""
Updates the thumbnail of a stream periodically
"""
if get_streamer_live_status(user_id)['is_live']:
print("Updating thumbnail...")
generate_thumbnail(stream_file, thumbnail_file)
update_thumbnail.apply_async((user_id, stream_file, thumbnail_file, sleep_time), countdown=sleep_time)
else:
print("Stream has ended, stopping thumbnail updates")
@shared_task
def combine_ts_stream(stream_path, vods_path, vod_file_name):
"""
Combines all ts files into a single vod, and removes the ts files
"""
ts_files = [f for f in listdir(stream_path) if f.endswith(".ts")]
ts_files.sort()
# Create temp file listing all ts files
with open(f"{stream_path}/list.txt", "w") as f:
for ts_file in ts_files:
f.write(f"file '{ts_file}'\n")
# Concatenate all ts files into a single vod
vod_command = [
"ffmpeg",
"-f",
"concat",
"-safe",
"0",
"-i",
f"{stream_path}/list.txt",
"-c",
"copy",
f"{vods_path}/{vod_file_name}.mp4"
]
subprocess.run(vod_command)
# Remove ts files
for ts_file in ts_files:
remove(f"{stream_path}/{ts_file}")
# Remove m3u8 file
remove(f"{stream_path}/index.m3u8")

View File

@@ -0,0 +1,71 @@
from celery import Celery, shared_task, Task
from datetime import datetime
from celery_tasks.preferences import user_preferences
from utils.stream_utils import generate_thumbnail, get_streamer_live_status
from time import sleep
from os import listdir, remove
import subprocess
@shared_task
def update_thumbnail(user_id, stream_file, thumbnail_file, sleep_time) -> None:
"""
Updates the thumbnail of a stream periodically
"""
if get_streamer_live_status(user_id)['is_live']:
print("Updating thumbnail...")
generate_thumbnail(stream_file, thumbnail_file)
update_thumbnail.apply_async((user_id, stream_file, thumbnail_file, sleep_time), countdown=sleep_time)
else:
print("Stream has ended, stopping thumbnail updates")
@shared_task
def combine_ts_stream(stream_path, vods_path, vod_file_name):
"""
Combines all ts files into a single vod, and removes the ts files
"""
ts_files = [f for f in listdir(stream_path) if f.endswith(".ts")]
ts_files.sort()
# Create temp file listing all ts files
with open(f"{stream_path}/list.txt", "w") as f:
for ts_file in ts_files:
f.write(f"file '{ts_file}'\n")
# Concatenate all ts files into a single vod
vod_command = [
"ffmpeg",
"-f",
"concat",
"-safe",
"0",
"-i",
f"{stream_path}/list.txt",
"-c",
"copy",
f"{vods_path}/{vod_file_name}.mp4"
]
subprocess.run(vod_command)
# Remove ts files
for ts_file in ts_files:
remove(f"{stream_path}/{ts_file}")
# Remove m3u8 file
remove(f"{stream_path}/index.m3u8")
@shared_task
def convert_image_to_png(image_path, png_path):
"""
Converts an image to a png
"""
image_command = [
"ffmpeg",
"-y",
"-i",
image_path,
png_path
]
subprocess.run(image_command)

View File

@@ -1,20 +1,53 @@
import os
# Description: This file contains the PathManager class which is responsible for managing the paths of the stream data.
class PathManager():
def __init__(self) -> None:
self.root_path = "stream_data"
self.vods_path = os.path.join(self.root_path, "vods")
self.stream_path = os.path.join(self.root_path, "stream")
self.profile_pictures_path = os.path.join(self.root_path, "profile_pictures")
self._create_root_directories()
def _create_root_directories(self):
"""
Create directories for stream data if they do not exist
"""
if not os.path.exists(self.vods_path):
os.makedirs(self.vods_path)
if not os.path.exists(self.stream_path):
os.makedirs(self.stream_path)
if not os.path.exists(self.profile_pictures_path):
os.makedirs(self.profile_pictures_path)
# Fix permissions
os.chmod(self.vods_path, 0o777)
os.chmod(self.stream_path, 0o777)
os.chmod(self.profile_pictures_path, 0o777)
def get_vods_path(self, username):
return f"stream_data/vods/{username}"
return os.path.join(self.vods_path, username)
def get_stream_path(self, username):
return f"stream_data/stream/{username}"
return os.path.join(self.stream_path, username)
def get_stream_file_path(self, username):
return f"{self.get_stream_path(username)}/index.m3u8"
return os.path.join(self.get_stream_path(username), "index.m3u8")
def get_current_stream_thumbnail_file_path(self, username):
return f"{self.get_stream_path(username)}/index.jpg"
return os.path.join(self.get_stream_path(username), "index.png")
def get_vod_file_path(self, username, vod_id):
return f"{self.get_vods_path(username)}/{vod_id}.mp4"
return os.path.join(self.get_vods_path(username), f"{vod_id}.mp4")
def get_vod_thumbnail_file_path(self, username, vod_id):
return f"{self.get_vods_path(username)}/{vod_id}.jpg"
return os.path.join(self.get_vods_path(username), f"{vod_id}.png")
def get_profile_picture_file_path(self, username):
return os.path.join(self.profile_pictures_path, f"{username}.png")
def get_profile_picture_path(self):
return self.profile_pictures_path

View File

@@ -151,7 +151,7 @@ def get_vod_tags(vod_id: int):
""", (vod_id,))
return tags
def create_local_directories(username: str):
def create_user_directories(username: str):
"""
Create directories for user stream data if they do not exist
"""