diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml
index ff2c6a5..6730e69 100644
--- a/docker-compose.dev.yaml
+++ b/docker-compose.dev.yaml
@@ -9,16 +9,20 @@ services:
- postgres
- redis
environment:
- DEBUG: 1
- SECRET_KEY: "D4mn1t_Ch4nG3_M3!1!!"
- SQL_ENGINE: django.db.backends.postgresql
- SQL_USER: portier
- SQL_PASSWORD: portier
- SQL_DATABASE: portier
- SQL_HOST: postgres
- SQL_PORT: 5432
- REDIS_HOST: redis
- REDIS_PORT: 6379
+ - DEBUG=1
+ - "SECRET_KEY=D4mn1t_Ch4nG3_M3!1!!"
+ - SQL_ENGINE=django.db.backends.postgresql
+ - SQL_USER=portier
+ - SQL_PASSWORD=portier
+ - SQL_DATABASE=portier
+ - SQL_HOST=postgres
+ - SQL_PORT=5432
+ - REDIS_HOST=redis
+ - REDIS_PORT=6379
+ - "EMAIL_FROM=${EMAIL_FROM}"
+ - "EMAIL_HOST=${EMAIL_HOST}"
+ - "EMAIL_HOST_USER=${EMAIL_HOST_USER}"
+ - "EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD}"
redis:
image: redis:5-alpine
postgres:
diff --git a/portal/templates/portal/index.html b/portal/templates/portal/index.html
index db1c7aa..b3f25f0 100644
--- a/portal/templates/portal/index.html
+++ b/portal/templates/portal/index.html
@@ -1,9 +1,11 @@
{% extends 'base.html' %}
-{% block 'body' %}
-
- Portier
- Nothing to see here yet. We're working on it, though!
- See progress on GitHub
-
+{% block 'content' %}
+
+
Hello, world!
+
Nothing to see here yet. We're working on it, though!
+
+
Portier will be a pretty cool streaming platform, built by and for nerds. You are welcome to contribute, be it code, ideas, documentation, design or something else entirely.
+
Code on GitHub
+
{% endblock %}
diff --git a/portier/settings.py b/portier/settings.py
index a8cb975..03a9577 100644
--- a/portier/settings.py
+++ b/portier/settings.py
@@ -36,6 +36,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'django_registration',
'bootstrap4',
'fa',
'portal.apps.PortalConfig',
@@ -108,26 +109,37 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
+# Django-Registration
+
+ACCOUNT_ACTIVATION_DAYS = 7
+
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
-
TIME_ZONE = 'UTC'
-
USE_I18N = True
-
USE_L10N = True
-
USE_TZ = True
-
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
+STATICFILES_DIRS = [
+ os.path.join(BASE_DIR, "static"),
+]
+
+
+# E-mail
+DEFAULT_FROM_EMAIL = os.environ.get('EMAIL_FROM')
+EMAIL_HOST = os.environ.get('EMAIL_HOST')
+EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
+EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
+EMAIL_PORT = os.environ.get('EMAIL_PORT', default=465)
+EMAIL_USE_SSL = int(os.environ.get('EMAIL_USE_SSL', default=1))
# Celery
diff --git a/portier/urls.py b/portier/urls.py
index c97f89b..5723019 100644
--- a/portier/urls.py
+++ b/portier/urls.py
@@ -17,6 +17,8 @@ from django.contrib import admin
from django.urls import include, path
urlpatterns = [
+ path('accounts/', include('django_registration.backends.activation.urls')),
+ path('accounts/', include('django.contrib.auth.urls')),
path('admin/', admin.site.urls),
path('rtmp/', include('rtmp.urls')),
path('concierge/', include('concierge.urls')),
diff --git a/requirements.txt b/requirements.txt
index 4817800..7643576 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
django>=3.0
+django-registration>=3.1
django-bootstrap4
django-fa
celery>=4.4
diff --git a/restream/models.py b/restream/models.py
index fa2f286..482b165 100644
--- a/restream/models.py
+++ b/restream/models.py
@@ -11,5 +11,8 @@ class RestreamConfig(models.Model):
name = models.CharField(max_length=100)
active = models.BooleanField()
+ def class_name(self):
+ return self.__class__.__name__
+
def __str__(self):
return '{} to {}'.format(self.stream, self.name)
diff --git a/rtmp/models.py b/rtmp/models.py
index 0828e99..597daa2 100644
--- a/rtmp/models.py
+++ b/rtmp/models.py
@@ -1,4 +1,5 @@
from django.db import models
+from django.urls import reverse
from django.utils.translation import gettext as _
import uuid
@@ -8,6 +9,9 @@ from . import signals
class Application(models.Model):
name = models.CharField(max_length=100, unique=True, help_text=_("rtmp_application_name"))
+ def class_name(self):
+ return self.__class__.__name__
+
def __str__(self):
return self.name
@@ -50,5 +54,11 @@ class Stream(models.Model):
)
self.save()
+ def get_absolute_url(self):
+ return reverse('rtmp:stream_detail', kwargs={'pk': self.pk})
+
+ def class_name(self):
+ return self.__class__.__name__
+
def __str__(self):
return self.name
diff --git a/rtmp/templates/rtmp/stream_confirm_delete.html b/rtmp/templates/rtmp/stream_confirm_delete.html
new file mode 100644
index 0000000..4bf9438
--- /dev/null
+++ b/rtmp/templates/rtmp/stream_confirm_delete.html
@@ -0,0 +1,44 @@
+{% extends 'base.html' %}
+{% load bootstrap4 %}
+{% load font_awesome %}
+{% block 'content' %}
+ Create new stream configuration
+
+
+
+
+
Deleting configurations
+
+ {% for object in to_delete %}
+ {% if object.name %}
+
+ {{ object.name }} - {{ object.class_name }}
+
+ {% else %}
+
+ {% for nested_object in object %}
+
+ {{ nested_object.name }} - {{ nested_object.class_name }}
+
+ {% endfor %}
+
+ {% endif %}
+ {% endfor %}
+
+
+
+{% endblock %}
diff --git a/rtmp/templates/rtmp/stream_detail.html b/rtmp/templates/rtmp/stream_detail.html
new file mode 100644
index 0000000..b4e9613
--- /dev/null
+++ b/rtmp/templates/rtmp/stream_detail.html
@@ -0,0 +1,71 @@
+{% extends 'base.html' %}
+{% load bootstrap4 %}
+{% load font_awesome %}
+{% block 'content' %}
+ Show stream configuration details
+
+
+
+
+ Name
+ {{ object.name }}
+ Application
+ {{ object.application }}
+
+
Do not share the stream ID. It serves as a secret key, and anyone that knows this key could send content to your stream.
+
+
+
How to configure your Encoder
+
+
Set the following stream server in your encoder application:
+
rtmp://TODO TODO SERVER BASE URL/{{ object.application }}/
+
Set the following stream ID in your encoder application:
+
+
You may need to use the full URL instead:
+
+
+
+
+{% endblock %}
diff --git a/rtmp/templates/rtmp/stream_form.html b/rtmp/templates/rtmp/stream_form.html
new file mode 100644
index 0000000..8bc3e7c
--- /dev/null
+++ b/rtmp/templates/rtmp/stream_form.html
@@ -0,0 +1,23 @@
+{% extends 'base.html' %}
+{% load bootstrap4 %}
+{% block 'content' %}
+ Create new stream configuration
+
+
+
+
+
+
+
Enter a descriptive name, and select one of the applications.
+
+
+{% endblock %}
diff --git a/rtmp/templates/rtmp/stream_list.html b/rtmp/templates/rtmp/stream_list.html
new file mode 100644
index 0000000..e66ae94
--- /dev/null
+++ b/rtmp/templates/rtmp/stream_list.html
@@ -0,0 +1,38 @@
+{% extends 'base.html' %}
+{% load bootstrap4 %}
+{% load font_awesome %}
+{% block 'content' %}
+ Stream configurations
+
+
+
+
+
+ Name
+ Actions
+
+
+
+ {% for object in object_list %}
+
+ {{ object.name }}
+
+
+
+
+ {% endfor %}
+
+
+
+{% endblock %}
diff --git a/rtmp/urls.py b/rtmp/urls.py
index a80bfb0..5bd69c2 100644
--- a/rtmp/urls.py
+++ b/rtmp/urls.py
@@ -1,6 +1,12 @@
from django.urls import path
from . import views
+app_name = 'rtmp'
+
urlpatterns = [
path('callback/srs', views.callback_srs, name='callback_srs'),
+ path('streams/', views.StreamList.as_view(), name='stream_list'),
+ path('streams//', views.StreamDetail.as_view(), name='stream_detail'),
+ path('streams//delete', views.StreamDelete.as_view(), name='stream_delete'),
+ path('streams/create', views.StreamCreate.as_view(), name='stream_create'),
]
diff --git a/rtmp/views.py b/rtmp/views.py
index 2bf13bc..1f26e7f 100644
--- a/rtmp/views.py
+++ b/rtmp/views.py
@@ -1,9 +1,14 @@
import json
import logging
-from django.http import HttpResponse
-from django.views.decorators.csrf import csrf_exempt
from django.core.exceptions import ObjectDoesNotExist
+from django.http import HttpResponse
+from django.urls import reverse_lazy
+from django.contrib.auth.decorators import login_required
+from django.contrib.admin.utils import NestedObjects
+from django.utils.decorators import method_decorator
+from django.views.decorators.csrf import csrf_exempt
+from django.views.generic import ListView, DetailView, CreateView, DeleteView
from . import models
@@ -40,3 +45,36 @@ def callback_srs(request):
stream.on_unpublish(param=param)
return HttpResponse('0')
+
+
+@method_decorator(login_required, name='dispatch')
+class StreamList(ListView):
+ model = models.Stream
+
+
+@method_decorator(login_required, name='dispatch')
+class StreamDetail(DetailView):
+ model = models.Stream
+
+
+@method_decorator(login_required, name='dispatch')
+class StreamCreate(CreateView):
+ model = models.Stream
+ fields = ["name", "application"]
+
+
+@method_decorator(login_required, name='dispatch')
+class StreamDelete(DeleteView):
+ model = models.Stream
+ success_url = reverse_lazy('rtmp:stream_list')
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ collector = NestedObjects(using='default')
+ collector.collect([self.object])
+
+ context['to_delete'] = collector.nested()
+
+ print(context['to_delete'])
+ return context
diff --git a/static/.gitkeep b/static/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/templates/base.html b/templates/base.html
index 6066c36..86e68a3 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -1,3 +1,4 @@
+{% load static %}
{% load bootstrap4 %}
{% load font_awesome %}
@@ -6,7 +7,7 @@
-
+
{% block 'title' %}portier{% endblock %}
{% bootstrap_css %}
@@ -15,8 +16,51 @@
- {% bootstrap_messages %}
- {% block 'body' %}
+
+
+
+ Portier
+
+
+
+
+
+
+
+
+ {% if user.is_authenticated %}
+
+
+ Configuration
+
+
+
+ {% endif %}
+
+ {% if user.is_authenticated %}
+ Logout
+ {% else %}
+ Login
+ {% endif %}
+
+
+
+
+
+
+ {% block 'content' %}
{% endblock %}
+
+