This commit is contained in:
2025-09-09 11:09:43 +04:00
parent 458486486d
commit 0145de19a3
54 changed files with 11278 additions and 1 deletions

1
server/__init__.py Normal file
View File

@@ -0,0 +1 @@
# This file makes the 'server' directory a Python package.

56
server/install.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script directory
echo -e "${BLUE}🔧 Setting up Meet Client & Server Environment${NC}"
echo "=================================================="
# Check if uv is installed
if ! command -v uv &> /dev/null; then
echo -e "${YELLOW}⚠️ uv is not installed. Installing uv...${NC}"
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.cargo/env
echo -e "${GREEN}✅ uv installed${NC}"
fi
echo -e "${GREEN}✅ uv found${NC}"
# Initialize uv project if not already done
if [ ! -f "pyproject.toml" ]; then
echo -e "${YELLOW}⚠️ No pyproject.toml found. Initializing uv project...${NC}"
uv init --no-readme --python 3.12
echo -e "${GREEN}✅ uv project initialized${NC}"
fi
# Sync dependencies
# echo -e "${YELLOW}📦 Installing dependencies with uv...${NC}"
# uv sync
# uv pip install -e .
# if [ -d "$HOME/code/git.ourworld.tf/herocode/herolib_python/herolib" ]; then
# echo -e "${GREEN}✅ Found local herolib, installing...${NC}"
# uv pip install -e "$HOME/code/git.ourworld.tf/herocode/herolib_python"
# else
# echo -e "${YELLOW}📦 Local herolib not found, installing from git...${NC}"
# uv pip install herolib@git+https://git.ourworld.tf/herocode/herolib_python.git --force-reinstall --no-cache-dir
# fi
uv pip install livekit-api livekit
uv pip install fastapi uvicorn python-dotenv
echo -e "${GREEN}✅ Dependencies installed${NC}"

1
server/lib/__init__.py Normal file
View File

@@ -0,0 +1 @@
# This file makes the 'lib' directory a Python package.

36
server/lib/livekit.py Normal file
View File

@@ -0,0 +1,36 @@
import os
from livekit import api
LIVEKIT_URL = os.getenv("LIVEKIT_URL")
LIVEKIT_API_KEY = os.getenv("LIVEKIT_API_KEY")
LIVEKIT_API_SECRET = os.getenv("LIVEKIT_API_SECRET")
lkapi = api.LiveKitAPI(LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET)
def create_access_token(identity: str, name: str, metadata: str, room_name: str) -> str:
token = (
api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET)
.with_identity(identity)
.with_name(name)
.with_metadata(metadata)
.with_grants(
api.VideoGrants(
room_join=True,
room=room_name,
can_publish=True,
can_publish_data=True,
can_subscribe=True,
)
)
.to_jwt()
)
return token
async def create_room_if_not_exists(room_name: str):
try:
await lkapi.room.create_room(api.CreateRoomRequest(name=room_name))
except api.RoomError as e:
if "room already exists" in str(e):
pass # Room already exists, which is fine
else:
raise e

23
server/pipenv.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
export PYTHONPATH=$PYTHONPATH:$(pwd)/.env/lib/python3.12/site-packages
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Create virtual environment if it doesn't exist
if [ ! -d ".venv" ]; then
echo "📦 Creating Python virtual environment..."
uv venv
echo "✅ Virtual environment created"
else
echo "✅ Virtual environment already exists"
fi
LIVEKIT_API_KEY="APIDWF7v3onyfaz"
# LIVEKIT_API_SECRET= //NEEDS TO BE SET IN ENV
LIVEKIT_URL="wss://despiegk-3su8kqlm.livekit.cloud"
# Activate virtual environment
echo "🔄 Activating virtual environment..."
source .venv/bin/activate

6
server/pyproject.toml Normal file
View File

@@ -0,0 +1,6 @@
[project]
name = "meet"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.12"
dependencies = []

80
server/server.py Normal file
View File

@@ -0,0 +1,80 @@
import os
import random
import string
import logging
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from dotenv import load_dotenv
from lib import livekit
load_dotenv()
logging.basicConfig(level=logging.INFO)
LIVEKIT_URL = os.getenv("LIVEKIT_URL")
if not LIVEKIT_URL:
raise EnvironmentError("LIVEKIT_URL must be set")
app = FastAPI()
def random_string(length: int) -> str:
return "".join(random.choices(string.ascii_letters + string.digits, k=length))
# API Routes
@app.get("/api/connection-details")
async def connection_details(
roomName: str = Query(..., alias="roomName"),
participantName: str = Query(..., alias="participantName"),
metadata: str = Query(""),
):
try:
random_postfix = random_string(4)
identity = f"{participantName}__{random_postfix}"
token = livekit.create_access_token(identity, participantName, metadata, roomName)
return {
"serverUrl": LIVEKIT_URL,
"roomName": roomName,
"participantToken": token,
"participantName": participantName,
}
except Exception as e:
logging.error(f"Error creating token: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/rooms/{room_name}", response_class=HTMLResponse)
async def create_room(room_name: str):
try:
await livekit.create_room_if_not_exists(room_name)
# Return HTML that loads the LiveKit component for this room
html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LiveKit Meet - {room_name}</title>
<link rel="stylesheet" href="/bundle.css" />
</head>
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
<script>
// Set the current path so the router knows we're on a room page
window.history.replaceState(null, '', '/rooms/{room_name}');
LiveKitMeet.render(document.getElementById('root'));
</script>
</body>
</html>
"""
return html_content
except Exception as e:
logging.error(f"Error creating room: {e}")
raise HTTPException(status_code=500, detail=str(e))
# Serve the static files from the 'public' directory, with html=True to handle SPA routing
app.mount("/", StaticFiles(directory="../public", html=True), name="static")

8
server/uv.lock generated Normal file
View File

@@ -0,0 +1,8 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "meet"
version = "0.1.0"
source = { virtual = "." }