Building Real-Time Communication: Harnessing WebRTC with FastAPI Part 2

wassafshahzad

Wassaf Shahzasd

Posted on March 10, 2024

Building Real-Time Communication: Harnessing WebRTC with FastAPI Part 2

Welcome to the second part of my series where we will be building a google-meet-clone using FastAPI and WebRTC. If you haven't read the previous article you can read it here.

In this tutorial we will be going through the ๐Ÿ’ป meat of things.

๐Ÿ”ฎ On the last episode of DBZ

When we last left, we created a basic hello world app in FastAPI and provided an introductory overview of how WebRTC functions.
Our Folder structure looked something like this.
๐Ÿ“
|-- ๐Ÿ—‹ main.py
|-- ๐Ÿ—‹ requirements.py
|-- ๐Ÿ“ .env

๐Ÿ’ผ Business at the Front.

So first lets get started with front-end. As described in the previous tutorial we will be using Jinja templates. So create a templates folder in your root directory. The folder structure then becomes the following.

๐Ÿ“
|-- ๐Ÿ—‹ main.py
|-- ๐Ÿ—‹ requirements.py
|-- ๐Ÿ“ templates
|-- ๐Ÿ“ .env

You can name this folder whatever you want but for consistency, I will be using templates. You can get an overview of jinja template from here. Don't worry about python dependencies, we handled that in the previous tutorial.

Now create a main.html file in templates directory. This will be our entry point and any JS or CSS dependencies or stylesheets will be loaded here.

Paste to following code in main.html, We will go over the important part line by line.

<!DOCTYPE html>
<html>

<head>
    <meta charset='utf-8'>
    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
    <title>PeerChat</title>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    {% block script %} {% endblock %}
    <link href=" https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
    {% block content %} {% endblock %}
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
Enter fullscreen mode Exit fullscreen mode

The first important part is the {% block script %} {% endblock %}.
This is a Jinja inheritance, which allows us to use this main.html as a parent for other templates. Since each child template will need to load its on scripts tags, we use this feature to dynamically load those. You can find more info here.
Other then that we are just loading some bootstrap dependencies and creating a block for out content using jinja inheritance mentioned above.

๐Ÿš€ Creating the Home Page

We will be creating a simple Home page with a title, input field and button.
For this create a home.html inside the the template directory.
๐Ÿ“
|-- ๐Ÿ—‹ main.py
|-- ๐Ÿ—‹ requirements.py
|-- ๐Ÿ“ templates
|-- |-- main.html
|-- |-- home.html
|-- ๐Ÿ“ .env

with the following code.

{% extends "main.html" %}
{% block script  %}
    <link rel='stylesheet' type='text/css' media='screen' href="{{ url_for('static', path='/home.css') }}">
    <script src="{{ url_for('static', path='/home.js') }}""></script>
{% endblock %}
{% block content %}
    <div class="main-container">
            <div>
                <h1>Video Call for Every One</h1>
                <div class="input-row">
                    <div>
                        <button  type="button" class="btn btn-success" onclick="createRoom()">Create room</button>
                    </div>
                    <div class="input-container">
                        <input id="createRoom" class="form-control" placeholder="Enter room name">
                    </div>
                </div>
            </div>
    </div>
{% endblock %}

Enter fullscreen mode Exit fullscreen mode

Don't worry about the CSS or the JS, we will create that later.
Know open up the ๐Ÿ main.py and use the following code to load and return templates
Add the following imports

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.templating import Jinja2Templates
Enter fullscreen mode Exit fullscreen mode

We will be using Jinja2Templates here to load the templates.
The following code loads the templates object from the templates directory.

templates = Jinja2Templates(directory="templates")
Enter fullscreen mode Exit fullscreen mode

Add the following code to enable CORS Middleware. Since we will be making requests moving forward.

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Enter fullscreen mode Exit fullscreen mode

Now lets update the read_root function and rename it as well while we are at it.

@app.get("/")
def home(request: Request):
    return templates.TemplateResponse(request=request, name="home.html")
Enter fullscreen mode Exit fullscreen mode

Run the fast application and you should get the following page.

Image description

๐Ÿงฉ Adding Static files

As you can see we need to add a bit of styling and some onclick functionality to our home page. For that we will be using the static files.
Static files are files which are downloaded by the client on first load. Unlike templates which are sent from the server on a per request basis, static files are automatically downloading by the user client (browser) when the first connection is made.

Create a static directory in your root folder. The folder structure becomes something like this.
๐Ÿ“
|-- ๐Ÿ—‹ main.py
|-- ๐Ÿ—‹ requirements.py
|-- ๐Ÿ“ static
|-- ๐Ÿ“ templates
|-- |-- main.html
|-- |-- home.html
|-- ๐Ÿ“ .env

To load static files from the server, add the following code to main.py

from fastapi import staticfiles

app.mount("/static", staticfiles.StaticFiles(directory="static"), name="static")
Enter fullscreen mode Exit fullscreen mode

In the above code we are telling out application to mount the static directory under /static url.

Create a home.css, home.js file under the static directory and add the following code.
for home.css

.main-container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.main-container * {
    padding-top: 5%;
    padding-bottom: 5%;
}

.input-row {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

.input-container {
    width: 70%;
}
Enter fullscreen mode Exit fullscreen mode

In home.js, add the following

let createRoom = () => {
    const roomName = document.getElementById("createRoom").value
    if (roomName){
        window.location.href = `/room/${roomName}`
    }
    else {
        alert("Room Name cannot be empty")
    }
}
Enter fullscreen mode Exit fullscreen mode

The above is just a JS funtion which tells the browser to redirect to /room/roomName where roomName is the value given in the input field.
Update home.html with the following code

{% extends "main.html" %}
{% block script  %}
    <link rel='stylesheet' type='text/css' media='screen' href="{{ url_for('static', path='/home.css') }}">
    <script src="{{ url_for('static', path='/home.js') }}""></script>
{% endblock %}
{% block content %}
    <div class="main-container">
            <div>
                <h1>Video Call for Every One</h1>
                <div class="input-row">
                    <div>
                        <button  type="button" class="btn btn-success" onclick="createRoom()">Create room</button>
                    </div>
                    <div class="input-container">
                        <input id="createRoom" class="form-control" placeholder="Enter room name">
                    </div>
                </div>
            </div>
    </div>
{% endblock %}

Enter fullscreen mode Exit fullscreen mode

Here we update the onclick button property with out function and the script block with the valid static file paths. Now reload the fast app and you Would get Something like this.

Image description

๐Ÿงจ Setting up the Video Page.

Create a new template called video.html and paste the following code.

{% extends "main.html" %}
{% block script  %}
    <link rel='stylesheet' type='text/css' media='screen' href="{{ url_for('static', path='/index.css') }}">
    <script src="{{ url_for('static', path='/index.js') }}""></script>
{% endblock %}
{% block content %}
    <div id="videos">
        <video class="video-player" id="user-1" autoplay></video>
        <video class="video-player" id="user-2" autoplay></video>
    </div>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Here 2 video players for people who will join the video call.
Now for the css. Create a video.css in the static folder and paste the following code.

.main-container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.main-container * {
    padding-top: 5%;
    padding-bottom: 5%;
}

.input-row {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

.input-container {
    width: 70%;
}
Enter fullscreen mode Exit fullscreen mode

I think this is good enough for now. In the next tutorial we will wrapping up the RTCpeerConnection logic and the singling server logic.

๐Ÿ’– ๐Ÿ’ช ๐Ÿ™… ๐Ÿšฉ
wassafshahzad
Wassaf Shahzasd

Posted on March 10, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

ยฉ TheLazy.dev

About