...
This commit is contained in:
1
server/__init__.py
Normal file
1
server/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file makes the 'server' directory a Python package.
|
56
server/install.sh
Executable file
56
server/install.sh
Executable 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
1
server/lib/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file makes the 'lib' directory a Python package.
|
36
server/lib/livekit.py
Normal file
36
server/lib/livekit.py
Normal 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
23
server/pipenv.sh
Executable 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
6
server/pyproject.toml
Normal 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
80
server/server.py
Normal 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
8
server/uv.lock
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "meet"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
Reference in New Issue
Block a user