<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wassaf Shahzasd</title>
    <description>The latest articles on DEV Community by Wassaf Shahzasd (@wassafshahzad).</description>
    <link>https://dev.to/wassafshahzad</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F396370%2Fcf03097e-9e0e-44e5-b585-ccb45163a978.jpg</url>
      <title>DEV Community: Wassaf Shahzasd</title>
      <link>https://dev.to/wassafshahzad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wassafshahzad"/>
    <language>en</language>
    <item>
      <title>🕵🏽 Investigating python Callables : functions, methods and more...</title>
      <dc:creator>Wassaf Shahzasd</dc:creator>
      <pubDate>Wed, 17 Apr 2024 04:41:50 +0000</pubDate>
      <link>https://dev.to/wassafshahzad/investigating-python-callables-functions-methods-and-more-5cp1</link>
      <guid>https://dev.to/wassafshahzad/investigating-python-callables-functions-methods-and-more-5cp1</guid>
      <description>&lt;p&gt;The way python handles functions is a bit unique and the star ⭐ of the show is the "self" argument passed to every function defined within a class, But have you ever wondered, how self is passed to the function? We never pass it during the call. &lt;br&gt;
How does it get there ?  and how can we replicate it ?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oleqxx0q2ce6gtm2k8r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oleqxx0q2ce6gtm2k8r.gif" alt="wondering" width="200" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well in this article, we will try to understand and replicate it. Lets put the &lt;code&gt;fun in functions&lt;/code&gt;🎉.&lt;/p&gt;
&lt;h2&gt;
  
  
  🧩 Types of functions in Python.
&lt;/h2&gt;

&lt;p&gt;First, we need to understand is the &lt;code&gt;type&lt;/code&gt; of functions. When using mypy, we annotate them as a Callable but that is not a python standard type. To figure this out lets get coding.&lt;br&gt;
(I know that world is spelt wrong, GET OFF MY BACK)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F252g4row3jdipak8qnre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F252g4row3jdipak8qnre.png" alt="function type" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the above code gives us the following results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;class 'function'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bit anticlimactic, so a function is of type &lt;code&gt;function&lt;/code&gt;, what about methods (foreshadowing...) or functions within a classes ?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbv0eyxzdghcykyl57sz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbv0eyxzdghcykyl57sz.png" alt="functions in class" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the following code gives us a bit more idea. The Output shows us&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;bound method Foo.foo of &amp;lt;__main__.Foo object at 0x0000020B6B999D00&amp;gt;&amp;gt;
&amp;lt;class 'method'&amp;gt;
&amp;lt;function Foo.foo at 0x0000020B6B9B91C0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We learn 3 things from this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The type of a function belonging to a class is &lt;code&gt;method&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The reference of a method is a &lt;code&gt;bound method&lt;/code&gt; tied down to the instance.&lt;/li&gt;
&lt;li&gt;Interestingly, a &lt;code&gt;method&lt;/code&gt; on a class level is treated as a function.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤨 How does self get there ?
&lt;/h2&gt;

&lt;p&gt;When an instance of a class is created, the functions defined within them get replaced by descriptors, with the instance passed to them as the first argument.&lt;br&gt;
You can learn more about &lt;a href="https://docs.python.org/3/howto/descriptor.html"&gt;descriptors from here&lt;/a&gt;. The TLDR is, that they are getter and setters for instance attributes.&lt;br&gt;
Try the following code and see what happens 🤩&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycc2lnznxuvichti4syo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycc2lnznxuvichti4syo.png" alt="Descriptor code" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Checking the type of the function, reveals a funny thing. The type of the bar function has been changed to a &lt;code&gt;method&lt;/code&gt; and is bound to the int object 10.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgy430x38hgp0o10yhxbs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgy430x38hgp0o10yhxbs.png" alt="bound method to int" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 What if you don't want a bound method ?
&lt;/h2&gt;

&lt;p&gt;In the above example, We create a bound method to tie some arguments to the function. What if you don't want to do that ? What if you don't want to use the &lt;strong&gt;get&lt;/strong&gt; method ? How can we recreate the above functionality, Well we use &lt;em&gt;&lt;strong&gt;decorators&lt;/strong&gt;&lt;/em&gt; !!!!!.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gvjgdpx7iup98p9n12z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gvjgdpx7iup98p9n12z.png" alt="pseudo bound method using decorators" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't want to use the decorator synthetic sugar, you can use this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iqrar0l872j75qd2z58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iqrar0l872j75qd2z58.png" alt="Higher order functions" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  😎 One more thing.
&lt;/h2&gt;

&lt;p&gt;Like we saw above &lt;code&gt;methods&lt;/code&gt; are defined as a &lt;code&gt;function&lt;/code&gt; type at the class level. Which means the first argument is unbound and we can do  this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24bot0s6ai6u8jk1vwal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24bot0s6ai6u8jk1vwal.png" alt="Using method as a function" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎆 Conclusion
&lt;/h2&gt;

&lt;p&gt;Thanks for following this article and hope you learned something new about functions in python. Word of caution, don't use this in production level code, unless you want to annoy your co-workers, if so, go crazy.&lt;br&gt;
Just cause we could to these shenanigans, doesn't mean we  should.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sjjv9125crfnckkbz6i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sjjv9125crfnckkbz6i.gif" alt="Jeff-goldblum-meme" width="480" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>programming</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Building Real-Time Communication: Harnessing WebRTC with FastAPI Part 3- Wrapping Every thing up</title>
      <dc:creator>Wassaf Shahzasd</dc:creator>
      <pubDate>Sun, 07 Apr 2024 15:10:54 +0000</pubDate>
      <link>https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-3-wrapping-every-thing-up-35fb</link>
      <guid>https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-3-wrapping-every-thing-up-35fb</guid>
      <description>&lt;p&gt;Welcome to the last 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 &lt;a href="https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-2-18l2"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 On the previous tutorial.
&lt;/h2&gt;

&lt;p&gt;Our project directory looked something like this.&lt;br&gt;
📁&lt;br&gt;
|-- 🗋 main.py&lt;br&gt;
|-- 🗋 requirements.py&lt;br&gt;
|-- 📁 static&lt;br&gt;
|--   |-- home.css&lt;br&gt;
|--   |-- home.js&lt;br&gt;
|-- 📁 templates&lt;br&gt;
|--   |-- main.html&lt;br&gt;
|--   |-- home.html&lt;br&gt;
|-- 📁 .env&lt;/p&gt;

&lt;p&gt;Create a new file on the same level as &lt;code&gt;main.py&lt;/code&gt; called &lt;code&gt;manager.py&lt;/code&gt;. Here we will create a WebSocket manager which will allow users to join, leave and message in a chat room.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from fastapi.websockets import WebSocket

class SignalManager:
    def __init__(self):
        self.active_connections: list[WebSocket] = []

    @property
    def is_empty(self):
        return len(self.active_connections) == 0

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def send_personal_message(self, message: str, websocket: WebSocket):
        await websocket.send_json(message)

    async def broadcast(self, message: dict, websocket: WebSocket):
        for connection in self.active_connections:
            if connection != websocket:
                await connection.send_json(message)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Signal Manager class is responsible for accepting web socket connections and sending mass and/or personal messages. The active_connections property is responsible for managing all active connections. The broadcast function sends a message to all other web sockets in the room except the web socket which sent the message.&lt;/p&gt;

&lt;h3&gt;
  
  
  👨‍💼 Chat Room Manager Class
&lt;/h3&gt;

&lt;p&gt;Now create a RoomManager class since there can be multiple rooms with multiple connections. just below the SignalManager class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MeetingManager:
    def __init__(self) -&amp;gt; None:
        self.rooms: dict[str, SignalManager] = {} 

    async def join(self, id: str, websocket: WebSocket):
        if id in self.rooms:
            await self.rooms[id].connect(websocket)
        else:
            self.rooms[id] = SignalManager()
            await self.rooms[id].connect(websocket)
        await self.rooms[id].broadcast({"type":"USER_JOIN"}, websocket)

    def leave(self, id: str, websocket: WebSocket):
        self.rooms[id].disconnect(websocket)
        if self.rooms[id].is_empty:
            del self.rooms[id]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class is responsible to managing the chat rooms and for allowing users to join and disconnect a room.&lt;/p&gt;

&lt;h5&gt;
  
  
  One point of interest is scalable WebSocket . To implement scalable WebSocket's, We need to implement WebSocket with redis.
&lt;/h5&gt;

&lt;h2&gt;
  
  
  🔌 Plugging every thing together.
&lt;/h2&gt;

&lt;p&gt;Open up  &lt;code&gt;main.py&lt;/code&gt; and import the Meeting Manager class from &lt;code&gt;manager.py&lt;/code&gt; and create a manager instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from manager import MeetingManager

manager = MeetingManager()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets create a function which allows a WebSocket to join a room.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.websocket("/ws/{client_id}")
async def connet_websocket(websocket: WebSocket, client_id: str):
    await meeting_manager.join(client_id, websocket)
    try:
        while True:
            data = await websocket.receive_json()
            await meeting_manager.rooms[client_id].broadcast(data, websocket)
    except WebSocketDisconnect:
        meeting_manager.leave(client_id, websocket)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code accepts a WebSocket connection and then awaits until the WebSocket disconnects. Until then it waits to receive messages and broadcasts it to the whole room.&lt;br&gt;
Like I explained previously, we will be using WebSocket's as a signaling server to send both SDP offers and ICE candidates.&lt;/p&gt;

&lt;p&gt;Lets add the URL for the video page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.get("/room/{roomName}")
def get_video(request: Request, roomName:str):
    return templates.TemplateResponse(request=request, name="index.html")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the URL which the &lt;code&gt;home.js&lt;/code&gt; redirects to after your enter the meeting room name. Don't worry about the index.html, we will create that later.&lt;/p&gt;

&lt;h2&gt;
  
  
  📍Business on the front, Party on the back
&lt;/h2&gt;

&lt;p&gt;Now lets create the &lt;code&gt;index.html&lt;/code&gt; in the templates folder. This html file is responsible for rendering the video tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends "main.html" %}
{% block script  %}
    &amp;lt;link rel='stylesheet' type='text/css' media='screen' href="{{ url_for('static', path='/index.css') }}"&amp;gt;
    &amp;lt;script src="{{ url_for('static', path='/index.js') }}""&amp;gt;&amp;lt;/script&amp;gt;
{% endblock %}
{% block content %}
    &amp;lt;div id="videos"&amp;gt;
        &amp;lt;video class="video-player" id="user-1" autoplay&amp;gt;&amp;lt;/video&amp;gt;
        &amp;lt;video class="video-player" id="user-2" autoplay&amp;gt;&amp;lt;/video&amp;gt;
    &amp;lt;/div&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right now we are only working with the assumption that only two users will join the call, later on you can add on to this and create it for multiple video calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏆 Moving on to the Hard stuff.
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;index.js&lt;/code&gt; file inside the static folder. Our project directory becomes the following.&lt;br&gt;
📁&lt;br&gt;
|-- 🗋 main.py&lt;br&gt;
|-- 🗋 requirements.py&lt;br&gt;
|-- 📁 static&lt;br&gt;
|--   |-- home.css&lt;br&gt;
|--   |-- home.js&lt;br&gt;
|--   |-- index.js&lt;br&gt;
|-- 📁 templates&lt;br&gt;
|--   |-- main.html&lt;br&gt;
|--   |-- home.html&lt;br&gt;
|--   |-- index.html&lt;br&gt;
|-- 📁 .env&lt;/p&gt;

&lt;p&gt;Initializing some variables which will be used later on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let localStream;
let remoteStream;
let peerConnection;
let socket;
let makingOffer = false;
let polite = false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets create a connect function which connects the WebSocket to the chat room. So that we can use our FastAPI Server as the singling server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let connect = async (callback) =&amp;gt; {
  let roomName = window.location.pathname.split("/")[1];
  socket = new WebSocket(`ws://localhost:8000/ws/${roomName}"`);
  socket.onopen = async (_) =&amp;gt;  {
    await callback()
  };

  socket.onmessage = handleMessage;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function takes in a callback which is called once the socket is successfully connected. We also assign a helper function called &lt;code&gt;handleMessage&lt;/code&gt; to the onmessage event. Don't worry we will define the function later on.&lt;/p&gt;

&lt;p&gt;Now lets create a init function which is first run when the index.js file is loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let init = async () =&amp;gt; {
  localStream = await navigator.mediaDevices.getUserMedia({
    video: true,
    audio: false,

.
  });
  document.getElementById("user-1").srcObject = localStream;
  await connect(createStreams);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function does two things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It gets the Users Media object and assigns it to the video tag, defined in the index.html.&lt;/li&gt;
&lt;li&gt;It also calls the connect function, with the &lt;code&gt;createStreams&lt;/code&gt; function as a callback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📼 Creating Video Streams
&lt;/h3&gt;

&lt;p&gt;Lets create a function which creates peerConnection object and remote media streams.The full function looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
let createStreams = async () =&amp;gt; {
  peerConnection = new RTCPeerConnection(config);
  remoteStream = new MediaStream();

  localStream.getTracks().forEach((track) =&amp;gt; {
    peerConnection.addTrack(track, localStream);
  });

  // This function is called each time a peer connects.
  peerConnection.ontrack = (event) =&amp;gt; {
    console.log("adding track");
    event.streams[0]
      .getTracks()
      .forEach((track) =&amp;gt; remoteStream.addTrack(track));
  };

  peerConnection.onicecandidate = async (event) =&amp;gt; {
    if (event.candidate) {
      socket.send(
        JSON.stringify({ type: "candidate", candidate: event.candidate })
      );
    }
  };
  peerConnection.onnegotiationneeded = async () =&amp;gt; {
    try {
      makingOffer = true;
      await peerConnection.setLocalDescription();
      // signaler.send({ description: pc.localDescription });
      socket.send(
        JSON.stringify({
          type: "OFFER",
          message: peerConnection.localDescription,
        })
      );
    } catch (err) {
      console.error(err);
    } finally {
      makingOffer = false;
    }
  };

  document.getElementById("user-2").srcObject = remoteStream;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function first creates a peerConnection Object from the provided config and initializes a empty remoteStream.&lt;br&gt;
The Config object specifies various options but the one we care about are the stun servers which allow us to establish a peer-to-peer connection.&lt;br&gt;
Our &lt;code&gt;Config Object&lt;/code&gt; looks something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const config = {
  iceServers: [
    {
      urls: [
        "stun:stun1.l.google.com:19302",
        "stun:stun1.l.google.com:19302",
        "stun:stun2.l.google.com:19302",
      ],
    },
  ],
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next thing we do is get out local tracks and add them to the peerConnection so its available so that they are available to other.(The full function is available just above, this part is just to make it easier to follow)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  localStream.getTracks().forEach((track) =&amp;gt; {
    peerConnection.addTrack(track, localStream);
  });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I the next part of the function, we attach a callback to the &lt;code&gt;ontrack&lt;/code&gt; event which is triggered every time a remote peer connects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;peerConnection.ontrack = (event) =&amp;gt; {
    console.log("adding track");
    event.streams[0]
      .getTracks()
      .forEach((track) =&amp;gt; remoteStream.addTrack(track));
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we create an event handler to trickle ice candidates to the connected peers and we use the socket object to send the ice candidates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  peerConnection.onicecandidate = async (event) =&amp;gt; {
    if (event.candidate) {
      socket.send(
        JSON.stringify({ type: "candidate", candidate: event.candidate })
      );
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next part &lt;code&gt;peerConnection.onnegotiationneeded&lt;/code&gt; will be discussed next, lets just skip it for now. Finally we attack the remote stream the &lt;code&gt;user-2&lt;/code&gt; video object.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤝 The Perfect Negotiation Pattern
&lt;/h2&gt;

&lt;p&gt;For exchanging the SDP offers and answers we will be implementing the perfect negotiation pattern as described in the following &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation"&gt;article&lt;/a&gt;. The main gist of the article is that we will have two types of peers, A &lt;code&gt;polite peer&lt;/code&gt; which in case of Offer collision ignores its offers and accepts the other senders offer. An &lt;code&gt;impolite peer&lt;/code&gt; which in case keeps sending offers and in case of collision rudely ignores the others offer.&lt;br&gt;
The way you decide who is polite and not depends on you, In this tutorial if a user is already present in the call he will always be polite otherwise impolite.&lt;/p&gt;

&lt;p&gt;Lets go back to the &lt;code&gt;createStreams&lt;/code&gt; functions and take a look on the &lt;code&gt;onnegotiationneeded&lt;/code&gt; event handler. This event in triggered in the following casses&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When your call &lt;code&gt;addTrack&lt;/code&gt; or &lt;code&gt;removeTrack&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When track track constraints are changed&lt;/li&gt;
&lt;li&gt;When you call setLocalDescription()&lt;/li&gt;
&lt;li&gt;When you  explicitly requests renegotiation by calling createOffer() or createAnswer()
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; peerConnection.onnegotiationneeded = async () =&amp;gt; {
    try {
      makingOffer = true;
      await peerConnection.setLocalDescription();
      socket.send(
        JSON.stringify({
          type: "OFFER",
          message: peerConnection.localDescription,
        })
      );
    } catch (err) {
      console.error(err);
    } finally {
      makingOffer = false;
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;First we set our local description and then send a message of type &lt;code&gt;OFFER&lt;/code&gt; to other peers.&lt;/p&gt;
&lt;h2&gt;
  
  
  🪁 Handling the messages
&lt;/h2&gt;

&lt;p&gt;If we go back to the &lt;code&gt;connect&lt;/code&gt; function, we see that we attach a event handler called &lt;code&gt;handleMessage&lt;/code&gt; to the onmessage event. This function will handle ice candidates and the SDP Answer and Offer we receive from out custom singling server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let handleMessage = async ({ data }) =&amp;gt; {
  data = JSON.parse(data);
  if (data["type"] == "USER_JOIN") {
    polite = true
    createStreams();
  }
  if (data["type"] === "OFFER") {
    console.log("received offer")
    handlePerfectNegotiation(data)
  }
  if (data["type"] === "ANSWER") {
    console.log("received answer")
    handlePerfectNegotiation(data)
  }
  if(data["type"] === "candidate") {
    handleIceCandidate(data)
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a relatively simple function, In case of a new user joining we set the &lt;code&gt;polite&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; which is &lt;code&gt;false&lt;/code&gt; by default.&lt;br&gt;
In case of the &lt;code&gt;ANSWER&lt;/code&gt; or &lt;code&gt;OFFER&lt;/code&gt;, we call the &lt;code&gt;handlePerfectNegotiation&lt;/code&gt; function, This is the last piece of the puzzle. So lets go over it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let handlePerfectNegotiation = async ({ message }) =&amp;gt; {
  try {
    if (message) {
      const offerCollision =
        message.type === "offer" &amp;amp;&amp;amp;
        (makingOffer || peerConnection.signalingState !== "stable");

      ignoreOffer = !polite &amp;amp;&amp;amp; offerCollision;
      if (ignoreOffer) {
        return;
      }

      await peerConnection.setRemoteDescription(message);
      if (message.type === "offer") {
        await peerConnection.setLocalDescription();
        socket.send(JSON.stringify({
          type: "ANSWER",
          message: peerConnection.localDescription,
        }));
      }
    }
  } catch (err) {
    console.error(err);
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function, when called from the side of the impolite peer ignores collision and keeps sending SDP offers and incase of the polite peer it acknowledges the collision, retracts its offers  and accepts the offer of the impolite peer.&lt;br&gt;
Now all we need is to handle the ice candidates and we are good to go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let handleIceCandidate = async ({ candidate }) =&amp;gt; {
  if (peerConnection &amp;amp;&amp;amp; peerConnection.remoteDescription) {
    peerConnection.addIceCandidate(candidate);
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tying everything together with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.addEventListener(
  "DOMContentLoaded",
  async function () {
    await init();
  },
  false
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and Boom we are done 🥳🥳🥳🥳🥳🥳🥳&lt;/p&gt;

&lt;p&gt;Thank you for following along and please share your feedback if you have any.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Best Practices
&lt;/h2&gt;

&lt;p&gt;In this article we have not followed some best practices, In a production environment. The websocket URL would not be hardcoded and on disconnect we would remove the streams. &lt;/p&gt;

&lt;h2&gt;
  
  
  Useful Links.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MDN  WebRTC &lt;a href="https://developer.mozilla.org/enUS/docs/Web/API/WebRTC_API/Perfect_negotiation"&gt;article&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Code Repository can be found &lt;a href="https://github.com/wassafshahzad/google-meet-clone"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demo &lt;a href="https://google-meet-clone-pipw.onrender.com/"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>javascript</category>
      <category>fastapi</category>
    </item>
    <item>
      <title>Building Real-Time Communication: Harnessing WebRTC with FastAPI Part 2</title>
      <dc:creator>Wassaf Shahzasd</dc:creator>
      <pubDate>Sun, 10 Mar 2024 16:35:48 +0000</pubDate>
      <link>https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-2-18l2</link>
      <guid>https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-2-18l2</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-1-ael"&gt;Part 1 of the series.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial we will be going through the 💻 meat of things.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 On the last episode of DBZ
&lt;/h2&gt;

&lt;p&gt;When we last left, we created a basic hello world app in FastAPI and provided an introductory overview of how WebRTC functions.&lt;br&gt;
Our Folder structure looked something like this.&lt;br&gt;
📁&lt;br&gt;
|-- 🗋 main.py&lt;br&gt;
|-- 🗋 requirements.py&lt;br&gt;
|-- 📁 .env&lt;/p&gt;

&lt;h3&gt;
  
  
  💼 Business at the Front.
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;📁&lt;br&gt;
|-- 🗋 main.py&lt;br&gt;
|-- 🗋 requirements.py&lt;br&gt;
|-- 📁 templates&lt;br&gt;
|-- 📁 .env&lt;/p&gt;

&lt;p&gt;You can name this folder whatever you want but for consistency, I will be using templates. You can get an overview of jinja &lt;a href="https://jinja.palletsprojects.com/en/3.1.x/" rel="noopener noreferrer"&gt;template from here&lt;/a&gt;. Don't worry about python dependencies, we handled that in the previous tutorial.&lt;/p&gt;

&lt;p&gt;Now create a &lt;code&gt;main.html&lt;/code&gt; file in templates directory. This will be our entry point and any JS or CSS dependencies or stylesheets will be loaded here.&lt;/p&gt;

&lt;p&gt;Paste to following code in &lt;code&gt;main.html&lt;/code&gt;, We will go over the important part line by line.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset='utf-8'&amp;gt;
    &amp;lt;meta http-equiv='X-UA-Compatible' content='IE=edge'&amp;gt;
    &amp;lt;title&amp;gt;PeerChat&amp;lt;/title&amp;gt;
    &amp;lt;meta name='viewport' content='width=device-width, initial-scale=1'&amp;gt;
    {% block script %} {% endblock %}
    &amp;lt;link href=" https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    {% block content %} {% endblock %}
    &amp;lt;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"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The first important part is the &lt;code&gt;{% block script %} {% endblock %}&lt;/code&gt;.&lt;br&gt;
This is a Jinja inheritance, which allows us to use this &lt;code&gt;main.html&lt;/code&gt; 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 &lt;a href="https://jinja.palletsprojects.com/en/3.1.x/templates/#template-inheritance" rel="noopener noreferrer"&gt;info here&lt;/a&gt;.&lt;br&gt;
Other then that we are just loading some bootstrap dependencies and creating a block for out content using jinja inheritance mentioned above.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Creating the Home Page
&lt;/h3&gt;

&lt;p&gt;We will be creating a simple Home page with a title, input field and button.&lt;br&gt;
For this create a &lt;code&gt;home.html&lt;/code&gt; inside the the template directory.&lt;br&gt;
📁&lt;br&gt;
|-- 🗋 main.py&lt;br&gt;
|-- 🗋 requirements.py&lt;br&gt;
|-- 📁 templates&lt;br&gt;
|--   |-- main.html&lt;br&gt;
|--   |-- home.html&lt;br&gt;
|-- 📁 .env&lt;/p&gt;

&lt;p&gt;with the following code.&lt;/p&gt;

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

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Don't worry about the CSS or the JS, we will create that later.&lt;br&gt;
Know open up the 🐍 &lt;code&gt;main.py&lt;/code&gt; and use the following code to load and return templates&lt;br&gt;
Add the following imports&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.templating import Jinja2Templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We will be using &lt;code&gt;Jinja2Templates&lt;/code&gt; here to load the templates.&lt;br&gt;
The following code loads the &lt;code&gt;templates&lt;/code&gt; object from the templates directory.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;templates = Jinja2Templates(directory="templates")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the following code to enable CORS Middleware. Since we will be making requests moving forward.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now lets update the read_root function and rename it as well while we are at it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.get("/")
def home(request: Request):
    return templates.TemplateResponse(request=request, name="home.html")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run the fast application and you should get the following page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiu52d51sh45qgdzg1wf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiu52d51sh45qgdzg1wf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧩 Adding Static files
&lt;/h3&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;strong&gt;Static files are files which are downloaded by the client on first load&lt;/strong&gt;. 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.&lt;/p&gt;

&lt;p&gt;Create a static directory in your root folder. The folder structure becomes something like this.&lt;br&gt;
📁&lt;br&gt;
|-- 🗋 main.py&lt;br&gt;
|-- 🗋 requirements.py&lt;br&gt;
|-- 📁 static&lt;br&gt;
|-- 📁 templates&lt;br&gt;
|--   |-- main.html&lt;br&gt;
|--   |-- home.html&lt;br&gt;
|-- 📁 .env&lt;/p&gt;

&lt;p&gt;To load static files from the server, add the following code to &lt;code&gt;main.py&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from fastapi import staticfiles

app.mount("/static", staticfiles.StaticFiles(directory="static"), name="static")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above code we are telling out application to mount the static directory under &lt;code&gt;/static&lt;/code&gt; url.&lt;/p&gt;

&lt;p&gt;Create a home.css, home.js file under the static directory and add the following code.&lt;br&gt;
for home.css&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.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%;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In home.js, add the following&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let createRoom = () =&amp;gt; {
    const roomName = document.getElementById("createRoom").value
    if (roomName){
        window.location.href = `/room/${roomName}`
    }
    else {
        alert("Room Name cannot be empty")
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

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

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qp3f4itqrxc1tfuu75j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qp3f4itqrxc1tfuu75j.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧨 Setting up the Video Page.
&lt;/h3&gt;

&lt;p&gt;Create a new template called &lt;code&gt;video.html&lt;/code&gt; and paste the following code.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends "main.html" %}
{% block script  %}
    &amp;lt;link rel='stylesheet' type='text/css' media='screen' href="{{ url_for('static', path='/index.css') }}"&amp;gt;
    &amp;lt;script src="{{ url_for('static', path='/index.js') }}""&amp;gt;&amp;lt;/script&amp;gt;
{% endblock %}
{% block content %}
    &amp;lt;div id="videos"&amp;gt;
        &amp;lt;video class="video-player" id="user-1" autoplay&amp;gt;&amp;lt;/video&amp;gt;
        &amp;lt;video class="video-player" id="user-2" autoplay&amp;gt;&amp;lt;/video&amp;gt;
    &amp;lt;/div&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here 2 video players for people who will join the video call.&lt;br&gt;
Now for the css. Create a video.css in the &lt;code&gt;static&lt;/code&gt; folder and paste the following code.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.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%;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I think this is good enough for now. In the next tutorial we will wrapping up the RTCpeerConnection logic and the singling server logic.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Real-Time Communication: Harnessing WebRTC with FastAPI Part 1</title>
      <dc:creator>Wassaf Shahzasd</dc:creator>
      <pubDate>Sun, 03 Mar 2024 00:25:55 +0000</pubDate>
      <link>https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-1-ael</link>
      <guid>https://dev.to/wassafshahzad/building-real-time-communication-harnessing-webrtc-with-fastapi-part-1-ael</guid>
      <description>&lt;p&gt;Welcome to my series of diving into the world of real time communication with Fast API and Vanilla Javascript (React will be introduced later...Maybe).&lt;br&gt;
In this tutorial we will be trying to make a real time video chat web application. In this tutorial we will try to accomplish the following goals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Covering the theory (Mostly)&lt;/li&gt;
&lt;li&gt;Initializing the project&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Technologies Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;For the Backend we will be using &lt;strong&gt;FastAPI&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;For the frontend side we will keep it simple and use &lt;strong&gt;vanilla JS and Jinja2 templates&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z5g6f24lgrrqu8bplu1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z5g6f24lgrrqu8bplu1.gif" alt="Lets get started"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What is WEBRTC
&lt;/h1&gt;

&lt;p&gt;WebRTC (Web Real-Time Communication) is a technology that enables Web applications and sites to capture and optionally stream audio and/or video media, as well as to exchange arbitrary data between browsers without requiring an intermediary. The set of standards that comprise WebRTC makes it possible to share data and perform teleconferencing peer-to-peer, without requiring that the user install plug-ins or any other third-party software. (Taken directly form MDN dont @ me)&lt;/p&gt;

&lt;p&gt;Keyword here being &lt;strong&gt;peer-to-peer&lt;/strong&gt;. Media streams and text can be sent from one connection to the other without the middle server (After initial connection). At this point you might be Wondering why not use Websockets ? I mean you already know WebSocket's and you know that you can create Realtime communication with WebSocket's too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why WebRTC and not WebSocket.
&lt;/h3&gt;

&lt;p&gt;The main difference between them is the middle man\server.&lt;br&gt;
In WebSockets, you connect to a server, You don't connect directly with a client\endpoint.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You send a web socket request to the backend server and the server keeps that request alive.&lt;/li&gt;
&lt;li&gt;When another user opens a WebSocket connection with the backend server, The backend servers keeps the connection alive and adds both to a room (depending on the implementation).&lt;/li&gt;
&lt;li&gt;Now when you send a message to the a user, It first goes the server, the server finds the valid Websockets and then forwards the message to the valid connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process does take some milliseconds which is fine for textual data but it can be an issue incase of voice and video streams.&lt;/p&gt;

&lt;p&gt;I case of WebRTC However data is directly send from one client to the other without the middle man saving us those precious few millisecond.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does WebRTC work
&lt;/h3&gt;

&lt;p&gt;This article is getting too long so lets just get the basics out to the way. Like explained before WebRTC works by establishing peer to peer connection. Now because of NATs, private networks direct connections cannot be established directly. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First we need to generate ICE candidates using STUN servers.&lt;/li&gt;
&lt;li&gt;The you need to send the peer connection offer and the ICE candidates to the callee.&lt;/li&gt;
&lt;li&gt;The other user then accepts the offer and sends his set of ice candidates to the caller.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;STUN servers are just servers which use the STUN protocol to establish a UPD connection between two users. In this tutorial we will be using free stun servers provided by google but you can set your up too.&lt;/p&gt;

&lt;p&gt;ICE candidates are basically offers describing how to connect two endpoints. They are generated using the STUN servers and we will see how the rtc generates them going forward.&lt;br&gt;
That's all for the theory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4hayzwldluyo5hb9p6e.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4hayzwldluyo5hb9p6e.gif" alt="Theory over image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the project
&lt;/h2&gt;

&lt;p&gt;We will be using python3+ for this tutorial, &lt;/p&gt;

&lt;p&gt;Create folder and name it what ever you like. I will be going with google meet clone.&lt;br&gt;
First lets create a virtualenv. Go to your newly created folder and open up the terminal by right clicking and selecting &lt;code&gt;open in terminal&lt;/code&gt;. Run the following command&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprf0j8vlesjqsdsemmso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprf0j8vlesjqsdsemmso.png" alt="Create env"&gt;&lt;/a&gt;&lt;br&gt;
Before running the above command make sure&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can access python 3+ by writing python in the terminal.&lt;/li&gt;
&lt;li&gt;You have venv installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a requirements.in file in the folder and paste the following dependencies&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxupqq810ubniv3jr2t0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxupqq810ubniv3jr2t0.png" alt="python dependencies"&gt;&lt;/a&gt;&lt;br&gt;
Then run the following command to install the dependencies&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sha8m3w5bjxnfeo9zpe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sha8m3w5bjxnfeo9zpe.png" alt="install command"&gt;&lt;/a&gt;&lt;br&gt;
Now create a &lt;code&gt;main.py&lt;/code&gt; and the root of your directory and paste the following command&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74tk5vhtpsuqaln7trms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74tk5vhtpsuqaln7trms.png" alt="Init code"&gt;&lt;/a&gt;&lt;br&gt;
Now your file directory should look something like this&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;google-meet-clone (what ever you named your project)&lt;br&gt;
|---- .env&lt;br&gt;
|---- main.py&lt;br&gt;
|---- requirements.in&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run the following command in the terminal &lt;br&gt;
&lt;code&gt;uvicorn main:app --reload&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Visit localhost:8000 and you should get a Hello world in JSON &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Although this tutorial was more theory than could but we successfully covered some basic contexts and have setup a FastAPI project.&lt;br&gt;
In the next tutorial we will get to the fun stuff&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.liveswitch.io/liveswitch-server/guides/what-are-stun-turn-and-ice.html" rel="noopener noreferrer"&gt;https://developer.liveswitch.io/liveswitch-server/guides/what-are-stun-turn-and-ice.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@iftimiealexandru/from-zero-to-hero-with-webrtc-in-javascript-and-python-in-small-snippets-of-code-part-1-1c4154d6ed9d" rel="noopener noreferrer"&gt;https://medium.com/@iftimiealexandru/from-zero-to-hero-with-webrtc-in-javascript-and-python-in-small-snippets-of-code-part-1-1c4154d6ed9d&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Live Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://google-meet-clone-pipw.onrender.com/lobby" rel="noopener noreferrer"&gt;https://google-meet-clone-pipw.onrender.com/lobby&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>python</category>
    </item>
    <item>
      <title>One click login using MetaMask with django-restframework and Web3</title>
      <dc:creator>Wassaf Shahzasd</dc:creator>
      <pubDate>Tue, 19 Apr 2022 00:51:42 +0000</pubDate>
      <link>https://dev.to/wassafshahzad/one-click-login-using-metamask-with-django-restframework-2b07</link>
      <guid>https://dev.to/wassafshahzad/one-click-login-using-metamask-with-django-restframework-2b07</guid>
      <description>&lt;p&gt;So, for a while now, I was looking for a way to create login and signup flow using blockchain and during my search I came across this fine article which details the authentication flow with great detail.&lt;br&gt;
&lt;a href="https://www.toptal.com/ethereum/one-click-login-flows-a-metamask-tutorial%60%60"&gt;https://www.toptal.com/ethereum/one-click-login-flows-a-metamask-tutorial``&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;But one problem its not in python 😤. Also you had to a lot tedious things to get it work. Only if some one would write a reusable application with all the boring bits already done.&lt;br&gt;
Well lucky for you dear reader. I am that guy and I wrote this nifty little DRF app to solve this problem.&lt;/p&gt;

&lt;p&gt;PYPI link : &lt;a href="https://pypi.org/project/django-metamask-auth/"&gt;https://pypi.org/project/django-metamask-auth/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get started go to the &lt;a href="https://github.com/wassafshahzad/django-metamask-auth"&gt;github repo&lt;/a&gt; and follow the getting started guide.&lt;/p&gt;

&lt;p&gt;Hopefully this helps some you and I will be working on an npm package for the front side, keep a look out for that as well and don't forget to star the repo.&lt;/p&gt;

</description>
      <category>django</category>
      <category>blockchain</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
