Skip to content

Commit

Permalink
Dyte private calls (ZcashFoundation#317)
Browse files Browse the repository at this point in the history
  • Loading branch information
skyl authored Jul 21, 2024
1 parent 9867d98 commit 08cb5ee
Show file tree
Hide file tree
Showing 12 changed files with 877 additions and 19,538 deletions.
4 changes: 2 additions & 2 deletions py/dj/apps/dyte/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def create_meeting(self, title) -> requests.Response:
"""
# url = /v2/meetings
# https://docs.dyte.io/api#/operations/create_meeting
url = f"https://api.dyte.io/v2/meetings"
payload = {
"title": title,
Expand Down Expand Up @@ -179,9 +180,8 @@ def add_participant(self, creator: Creator, meeting_id: str) -> requests.Respons

logger.info(f"meeting owner = {cm.creator.username}, participant = {creator.username}")

# Generate garbage values if user is anonymous
if not creator.is_authenticated:
username = f"anon-{uuid.uuid4()}"
username = f"Guest {uuid.uuid4()}"
picture_url = "https://free2z.com/logo512.png"
else:
username = creator.username
Expand Down
23 changes: 23 additions & 0 deletions py/dj/apps/dyte/migrations/0005_creatormeeting_secret_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.11 on 2024-07-20 06:38

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dyte', '0004_auto_20231111_1912'),
]

operations = [
migrations.AddField(
model_name='creatormeeting',
name='secret',
field=models.UUIDField(editable=False, help_text='Secret UUID used to join private', null=True, unique=True),
),
migrations.AlterField(
model_name='creatormeeting',
name='meeting_type',
field=models.CharField(choices=[('private', 'Private'), ('broadcast', 'Broadcast'), ('ppv', 'Pay Per View'), ('subscribers-only', 'Subscribers Only')], default='subscribers-only', max_length=20),
),
]
18 changes: 18 additions & 0 deletions py/dj/apps/dyte/migrations/0006_creatormeeting_e2ee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2024-07-20 19:59

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dyte', '0005_creatormeeting_secret_and_more'),
]

operations = [
migrations.AddField(
model_name='creatormeeting',
name='e2ee',
field=models.BooleanField(default=False),
),
]
13 changes: 13 additions & 0 deletions py/dj/apps/dyte/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import uuid
from django.db import models
from django.utils import timezone

# Meeting type constants
BROADCAST = 'broadcast'
PAY_PER_VIEW = 'ppv'
SUBSCRIBERS_ONLY = 'subscribers-only'
PRIVATE = 'private'
MEETING_TYPE_CHOICES = [
(PRIVATE, 'Private'),
(BROADCAST, 'Broadcast'),
(PAY_PER_VIEW, 'Pay Per View'),
(SUBSCRIBERS_ONLY, 'Subscribers Only'),
Expand All @@ -26,6 +29,11 @@ class CreatorMeeting(models.Model):
max_digits=6, decimal_places=2, default=0,
help_text="Optional fee set by Creator",
)
secret = models.UUIDField(
unique=True, null=True, editable=False,
help_text="Secret UUID used to join private"
)
e2ee = models.BooleanField(default=False)

class Meta:
# Exactly one private meeting of each type per creator
Expand All @@ -35,3 +43,8 @@ class Meta:

def __str__(self):
return f"{self.get_meeting_type_display()} Room for {self.creator.username}"

def recycle_secret(self):
self.secret = uuid.uuid4()
self.save()
return self.secret
7 changes: 7 additions & 0 deletions py/dj/apps/dyte/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,16 @@
views.live_meeting_status,
name='live_meeting_status'
),
path('<str:username>/private',
views.PrivateMeetingManage.as_view(),
name='private_meeting'),
path('<str:username>/private/<uuid:secret>',
views.PrivateMeeting.as_view(),
name='private_meeting'),
path(
'<str:username>/<str:type>',
views.DyteMeeting.as_view(),
name='dyte_meeting'
),

]
117 changes: 103 additions & 14 deletions py/dj/apps/dyte/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from rest_framework.response import Response
from rest_framework import status
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated

from gcp.pubsub.client import PubSubClient
from cryptography.exceptions import InvalidSignature
Expand All @@ -23,7 +24,7 @@
from dj.apps.g12f.models.creator import Creator, Subscription
from dj.apps.events.models import Event

from .models import CreatorMeeting
from .models import PRIVATE, CreatorMeeting
from .serializers import CreatorMeetingSerializer
from .client import DyteClient

Expand Down Expand Up @@ -295,6 +296,105 @@ def live_meeting_status(request, username):
return Response(resp)


def _create_meeting(cm: CreatorMeeting, dc: DyteClient = None) -> CreatorMeeting:
if not dc:
dc = DyteClient(settings.DYTE_ORG)
resp = dc.create_meeting(cm.creator.username)
d = resp.json()
if d.get('error'):
logger.error(d)
raise Exception("Dyte Error")
cm.meeting_id = d['data']['id']
cm.save()
return cm


class PrivateMeetingManage(APIView):
"""
GET will return the UUID for the currently logged in user's private meeting
POST will create a new private meeting for the currently logged in user,
if one does not already exist. Otherwise it will recycle the secret UUID
and return a new one
Only for the creator to manage their own private meeting
"""
permission_classes = [IsAuthenticated]

def get(self, request, *args, **kwargs):
cm = get_object_or_404(
CreatorMeeting,
creator=request.user,
meeting_type=PRIVATE,
)
return Response(data={
'secret': cm.secret,
'e2ee': cm.e2ee,
})

def post(self, request, *args, **kwargs):
stream_owner = kwargs.get('username')
if (
request.user.username == stream_owner and not
request.user.can_stream
):
return Response(
status=status.HTTP_400_BAD_REQUEST,
data="You need at least 150 to go live!"
)
cm, _ = CreatorMeeting.objects.get_or_create(
creator=request.user,
meeting_type=PRIVATE,
is_public=False,
)
cm = _create_meeting(cm)
cm.e2ee = request.data.get('e2ee', False)
cm.recycle_secret()
return Response(data={
'secret': cm.secret,
'e2ee': cm.e2ee,
})


class PrivateMeeting(APIView):
"""
Post will join the private meeting:
For the creator, it will join the meeting as the host
For the participant, it will join the meeting as a participant
"""

def post(self, request, *args, **kwargs):
dc = DyteClient(settings.DYTE_ORG)
stream_owner = kwargs.get('username')
secret = kwargs.get('secret')

if (
request.user.username == stream_owner and not
request.user.can_stream
):
return Response(
status=status.HTTP_400_BAD_REQUEST,
data="You need at least 150 to go live!"
)

cm = get_object_or_404(
CreatorMeeting,
creator__username=stream_owner,
meeting_type=PRIVATE,
secret=secret,
)
# if not cm.meeting_id:
# cm = _create_meeting(cm, dc)

resp = dc.add_participant(request.user, cm.meeting_id)
auth_token = resp.json()['data']['token']
return Response({
# "meeting_id": cm.meeting_id,
"e2ee": cm.e2ee,
"auth_token": auth_token,
})


class DyteMeeting(APIView):
"""
Create/join _public_ meetings
Expand Down Expand Up @@ -371,7 +471,7 @@ def post(self, request, *args, **kwargs):
if created or not cm.meeting_id or (
timezone.now() - cm.created_at > timezone.timedelta(days=1)
):
cm = self._create_meeting(cm, dc)
cm = _create_meeting(cm, dc)

logger.info(f"Adding Host! {request.user.username}")
resp = dc.add_participant(request.user, cm.meeting_id)
Expand All @@ -381,7 +481,7 @@ def post(self, request, *args, **kwargs):
# time if there is a problem?
if d.get('error'):
# {'success': False, 'error': {'code': 404, 'message': 'No ..
cm = self._create_meeting(cm, dc)
cm = _create_meeting(cm, dc)
# try once more
resp = dc.add_participant(request.user, cm.meeting_id)
# TODO
Expand Down Expand Up @@ -451,14 +551,3 @@ def post(self, request, *args, **kwargs):
"auth_token": auth_token,
})

def _create_meeting(self, cm: CreatorMeeting, dc: DyteClient = None) -> CreatorMeeting:
if not dc:
dc = DyteClient(settings.DYTE_ORG)
resp = dc.create_meeting(cm.creator.username)
d = resp.json()
if d.get('error'):
logger.error(d)
raise Exception("Dyte Error")
cm.meeting_id = d['data']['id']
cm.save()
return cm
Loading

0 comments on commit 08cb5ee

Please sign in to comment.