refactor: move rtmp, restream into config, portal into core
This commit is contained in:
parent
ba03d9be1a
commit
72824d32d4
|
@ -1,8 +1,8 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-26 17:25
|
# Generated by Django 5.0.2 on 2024-02-27 19:20
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import uuid
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('rtmp', '0002_stream_publish_counter'),
|
('config', '0001_initial'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
@ -18,27 +18,22 @@ class Migration(migrations.Migration):
|
||||||
name='Identity',
|
name='Identity',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('identity', models.CharField(default=uuid.uuid4, max_length=36, unique=True)),
|
('identity', models.UUIDField(default=uuid.uuid4, unique=True)),
|
||||||
('name', models.CharField(max_length=100)),
|
('name', models.CharField(max_length=100)),
|
||||||
('notes', models.TextField()),
|
('notes', models.TextField(blank=True)),
|
||||||
('heartbeat', models.DateTimeField(blank=True)),
|
('heartbeat', models.DateTimeField(blank=True, null=True)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Task',
|
name='Task',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True)),
|
||||||
('type', models.CharField(max_length=100)),
|
('type', models.CharField(max_length=100)),
|
||||||
|
('config_id', models.IntegerField()),
|
||||||
('configuration', models.TextField()),
|
('configuration', models.TextField()),
|
||||||
('stream', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rtmp.Stream')),
|
('claimed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='concierge.identity')),
|
||||||
],
|
('stream', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='config.stream')),
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Claim',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='concierge.Identity')),
|
|
||||||
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='concierge.Task')),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-26 18:34
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('concierge', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='claim',
|
|
||||||
name='id',
|
|
||||||
field=models.CharField(default=uuid.uuid4, max_length=36, primary_key=True, serialize=False, unique=True),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-26 18:35
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('concierge', '0002_auto_20200426_1834'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='identity',
|
|
||||||
name='heartbeat',
|
|
||||||
field=models.DateTimeField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='identity',
|
|
||||||
name='notes',
|
|
||||||
field=models.TextField(blank=True),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,28 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-26 19:12
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('concierge', '0003_auto_20200426_1835'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='task',
|
|
||||||
name='claimed_by',
|
|
||||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='concierge.Identity'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='task',
|
|
||||||
name='uuid',
|
|
||||||
field=models.UUIDField(default=uuid.uuid4, serialize=False, unique=True),
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='Claim',
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,30 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-26 20:07
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('concierge', '0004_auto_20200426_1912'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='identity',
|
|
||||||
name='identity',
|
|
||||||
field=models.UUIDField(default=uuid.uuid4, unique=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='task',
|
|
||||||
name='claimed_by',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='concierge.Identity'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='task',
|
|
||||||
name='uuid',
|
|
||||||
field=models.UUIDField(default=uuid.uuid4, unique=True),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,19 +0,0 @@
|
||||||
# Generated by Django 3.0.6 on 2020-05-31 09:51
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('concierge', '0005_auto_20200426_2007'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='task',
|
|
||||||
name='config_id',
|
|
||||||
field=models.IntegerField(default=0),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,6 +1,6 @@
|
||||||
import uuid
|
import uuid
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from rtmp.models import Stream
|
from config.models import Stream
|
||||||
|
|
||||||
|
|
||||||
class Identity(models.Model):
|
class Identity(models.Model):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from rtmp.signals import stream_inactive
|
|
||||||
from .models import Task
|
from .models import Task
|
||||||
from rtmp.models import Stream
|
from config.signals import stream_inactive
|
||||||
|
from config.models import Stream
|
||||||
|
|
||||||
|
|
||||||
@receiver(stream_inactive)
|
@receiver(stream_inactive)
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase # noqa
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from guardian.admin import GuardedModelAdmin
|
||||||
|
from .models import Stream, Restream
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Stream)
|
||||||
|
class StreamAdmin(GuardedModelAdmin):
|
||||||
|
fields = ['stream', 'name', 'publish_counter']
|
||||||
|
|
||||||
|
@admin.register(Restream)
|
||||||
|
class RestreamAdmin(GuardedModelAdmin):
|
||||||
|
fields = ['name', 'active', 'stream', 'format', 'target']
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'config'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
import config.signals, config.signals_shared # noqa
|
|
@ -3,9 +3,9 @@ from guardian.shortcuts import get_objects_for_user
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
class RestreamConfigFilteredStreamForm(ModelForm):
|
class RestreamFilteredStreamForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.RestreamConfig
|
model = models.Restream
|
||||||
fields = ['name', 'stream', 'target', 'format', 'active']
|
fields = ['name', 'stream', 'target', 'format', 'active']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -13,4 +13,4 @@ class RestreamConfigFilteredStreamForm(ModelForm):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# limit the stream selection to user-accessible streams
|
# limit the stream selection to user-accessible streams
|
||||||
self.fields['stream'].queryset = get_objects_for_user(user, 'rtmp.view_stream')
|
self.fields['stream'].queryset = get_objects_for_user(user, 'config.view_stream')
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Generated by Django 5.0.2 on 2024-02-27 19:20
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Stream',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('stream', models.UUIDField(default=uuid.uuid4, help_text='Stream ID for this stream', unique=True)),
|
||||||
|
('name', models.CharField(help_text='Name for this stream', max_length=100)),
|
||||||
|
('publish_counter', models.PositiveIntegerField(default=0)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Restream',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('target', models.CharField(help_text='restream_target_help', max_length=500)),
|
||||||
|
('name', models.CharField(help_text='restream_name_help', max_length=100)),
|
||||||
|
('active', models.BooleanField(help_text='restream_activate_help')),
|
||||||
|
('format', models.CharField(choices=[('flv', 'flv (RTMP)'), ('mpegts', 'mpegts (SRT)')], default='flv', help_text='restream_format_help', max_length=6)),
|
||||||
|
('stream', models.ForeignKey(help_text='restream_stream_help', on_delete=django.db.models.deletion.CASCADE, to='config.stream')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'restream_verbose_name',
|
||||||
|
'verbose_name_plural': 'restream_verbose_name_plural',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,30 +1,16 @@
|
||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
from portier.common import handlers
|
from portier.common import handlers
|
||||||
|
from . import signals_shared
|
||||||
from . import signals
|
|
||||||
|
|
||||||
|
|
||||||
class Application(models.Model):
|
|
||||||
name = models.CharField(max_length=100, unique=True, help_text=_("rtmp_application_name"))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('application_verbose_name')
|
|
||||||
verbose_name_plural = _('application_verbose_name_plural')
|
|
||||||
|
|
||||||
def class_name(self):
|
|
||||||
return _('aplication_class_name')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Stream(models.Model):
|
class Stream(models.Model):
|
||||||
application = models.ForeignKey(Application, on_delete=models.CASCADE, help_text=_('stream_application_help'))
|
|
||||||
stream = models.UUIDField(unique=True, default=uuid.uuid4, help_text=_('stream_stream_help'))
|
stream = models.UUIDField(unique=True, default=uuid.uuid4, help_text=_('stream_stream_help'))
|
||||||
name = models.CharField(max_length=100, help_text=_('stream_name_help'))
|
name = models.CharField(max_length=100, help_text=_('stream_name_help'))
|
||||||
|
|
||||||
|
@ -38,7 +24,7 @@ class Stream(models.Model):
|
||||||
# if so far there were less than one incoming streams, this stream
|
# if so far there were less than one incoming streams, this stream
|
||||||
# is now being considered active
|
# is now being considered active
|
||||||
if self.publish_counter < 1:
|
if self.publish_counter < 1:
|
||||||
signals.stream_active.send(sender=self.__class__,
|
signals_shared.stream_active.send(sender=self.__class__,
|
||||||
stream=str(self.stream),
|
stream=str(self.stream),
|
||||||
param=param
|
param=param
|
||||||
)
|
)
|
||||||
|
@ -55,14 +41,14 @@ class Stream(models.Model):
|
||||||
# if we now have less than one incoming stream, this stream is being
|
# if we now have less than one incoming stream, this stream is being
|
||||||
# considered inactive
|
# considered inactive
|
||||||
if self.publish_counter < 1:
|
if self.publish_counter < 1:
|
||||||
signals.stream_inactive.send(sender=self.__class__,
|
signals_shared.stream_inactive.send(sender=self.__class__,
|
||||||
stream=str(self.stream),
|
stream=str(self.stream),
|
||||||
param=param
|
param=param
|
||||||
)
|
)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('rtmp:stream_detail', kwargs={'pk': self.pk})
|
return reverse('config:stream_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
def class_name(self):
|
def class_name(self):
|
||||||
return _('stream_class_name')
|
return _('stream_class_name')
|
||||||
|
@ -71,5 +57,41 @@ class Stream(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Application)
|
|
||||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Stream)
|
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Stream)
|
||||||
|
|
||||||
|
class Restream(models.Model):
|
||||||
|
FORMATS = (
|
||||||
|
('flv', 'flv (RTMP)'),
|
||||||
|
('mpegts', 'mpegts (SRT)'),
|
||||||
|
)
|
||||||
|
stream = models.ForeignKey(Stream, on_delete=models.CASCADE, help_text=_('restream_stream_help'))
|
||||||
|
target = models.CharField(max_length=500, help_text=_('restream_target_help'))
|
||||||
|
name = models.CharField(max_length=100, help_text=_('restream_name_help'))
|
||||||
|
active = models.BooleanField(help_text=_('restream_activate_help'))
|
||||||
|
format = models.CharField(max_length=6, choices=FORMATS, default='flv', help_text=_('restream_format_help'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('restream_verbose_name')
|
||||||
|
verbose_name_plural = _('restream_verbose_name_plural')
|
||||||
|
|
||||||
|
def class_name(self):
|
||||||
|
return _('restream_class_name')
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('config:restream_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{} to {}'.format(self.stream, self.name)
|
||||||
|
|
||||||
|
def get_json_config(self):
|
||||||
|
config = {
|
||||||
|
'name': self.name,
|
||||||
|
'app': settings.GLOBAL_STREAM_NAMESPACE,
|
||||||
|
'stream': str(self.stream.stream),
|
||||||
|
'target': self.target,
|
||||||
|
'format': self.format
|
||||||
|
}
|
||||||
|
return json.dumps(config)
|
||||||
|
|
||||||
|
|
||||||
|
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Restream)
|
|
@ -1,22 +1,20 @@
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.db.models.signals import post_save, post_delete
|
from django.db.models.signals import post_save, post_delete
|
||||||
from rtmp.signals import stream_active
|
from .models import Restream, Stream
|
||||||
from .models import RestreamConfig
|
|
||||||
from rtmp.models import Stream
|
|
||||||
from concierge.models import Task
|
from concierge.models import Task
|
||||||
|
from .signals_shared import stream_active, stream_inactive
|
||||||
|
|
||||||
@receiver(stream_active)
|
@receiver(stream_active)
|
||||||
def create_tasks(sender, **kwargs):
|
def create_tasks(sender, **kwargs):
|
||||||
stream = Stream.objects.get(stream=kwargs['stream'])
|
stream = Stream.objects.get(stream=kwargs['stream'])
|
||||||
instances = RestreamConfig.objects.filter(active=True, stream=stream)
|
instances = Restream.objects.filter(active=True, stream=stream)
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
task = Task(stream=instance.stream, type='restream', config_id=instance.id,
|
task = Task(stream=instance.stream, type='restream', config_id=instance.id,
|
||||||
configuration=instance.get_json_config())
|
configuration=instance.get_json_config())
|
||||||
task.save()
|
task.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=RestreamConfig)
|
@receiver(post_save, sender=Restream)
|
||||||
def update_tasks(sender, **kwargs):
|
def update_tasks(sender, **kwargs):
|
||||||
instance = kwargs['instance']
|
instance = kwargs['instance']
|
||||||
# TODO: check for breaking changes using update_fields. This needs custom save_model functions though.
|
# TODO: check for breaking changes using update_fields. This needs custom save_model functions though.
|
||||||
|
@ -35,7 +33,7 @@ def update_tasks(sender, **kwargs):
|
||||||
task.save()
|
task.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=RestreamConfig)
|
@receiver(post_delete, sender=Restream)
|
||||||
def delete_tasks(sender, **kwargs):
|
def delete_tasks(sender, **kwargs):
|
||||||
instance = kwargs['instance']
|
instance = kwargs['instance']
|
||||||
# Get the current task instance if it exists, and remove it
|
# Get the current task instance if it exists, and remove it
|
|
@ -1,4 +1,4 @@
|
||||||
from django.dispatch import Signal
|
from django.dispatch import Signal
|
||||||
|
|
||||||
stream_active = Signal()
|
stream_active = Signal()
|
||||||
stream_inactive = Signal()
|
stream_inactive = Signal()
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="col-sm border-right">
|
<div class="col-sm border-right">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<p>{% blocktrans with restreamconfig_config_name=object.name %}are_you_sure_you_want_to_delete_"{{ restreamconfig_config_name }}"?{% endblocktrans %}</p>
|
<p>{% blocktrans with restream_config_name=object.name %}are_you_sure_you_want_to_delete_"{{ restream_config_name }}"?{% endblocktrans %}</p>
|
||||||
{% buttons %}
|
{% buttons %}
|
||||||
<button type="submit" class="btn btn-danger" value="login">
|
<button type="submit" class="btn btn-danger" value="login">
|
||||||
{% fa5_icon 'trash' %} {% trans "delete" %}
|
{% fa5_icon 'trash' %} {% trans "delete" %}
|
|
@ -9,15 +9,15 @@
|
||||||
{% block 'content' %}
|
{% block 'content' %}
|
||||||
<div class="row justify-content-between">
|
<div class="row justify-content-between">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h6>{% trans "restreamconfig_configuration_details_header" %}</h6>
|
<h6>{% trans "restream_configuration_details_header" %}</h6>
|
||||||
</div>
|
</div>
|
||||||
{% get_obj_perms user for object as "obj_perms" %}
|
{% get_obj_perms user for object as "obj_perms" %}
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
{% if "change_restreamconfig" in obj_perms %}
|
{% if "change_restream" in obj_perms %}
|
||||||
<a href="{% url 'restream:restreamconfig_change' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'edit' %} {% trans 'change' %}</a>
|
<a href="{% url 'config:restream_change' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'edit' %} {% trans 'change' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if "delete_restreamconfig" in obj_perms %}
|
{% if "delete_restream" in obj_perms %}
|
||||||
<a href="{% url 'restream:restreamconfig_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %} {% trans 'delete' %}</a>
|
<a href="{% url 'config:restream_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %} {% trans 'delete' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<dt class="col-sm-3">{% trans "name" %}</dt>
|
<dt class="col-sm-3">{% trans "name" %}</dt>
|
||||||
<dd class="col-sm-9">{{ object.name }}</dd>
|
<dd class="col-sm-9">{{ object.name }}</dd>
|
||||||
<dt class="col-sm-3">{% trans "stream" %}</dt>
|
<dt class="col-sm-3">{% trans "stream" %}</dt>
|
||||||
<dd class="col-sm-9"><a href="{% url 'rtmp:stream_detail' pk=object.stream.pk %}">{{ object.stream.name }}</a></dd>
|
<dd class="col-sm-9"><a href="{% url 'config:stream_detail' pk=object.stream.pk %}">{{ object.stream.name }}</a></dd>
|
||||||
<dt class="col-sm-3">{% trans "active" %}</dt>
|
<dt class="col-sm-3">{% trans "active" %}</dt>
|
||||||
<dd class="col-sm-9">
|
<dd class="col-sm-9">
|
||||||
{% if object.active %}
|
{% if object.active %}
|
|
@ -2,7 +2,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap4 %}
|
{% load bootstrap4 %}
|
||||||
{% block 'content' %}
|
{% block 'content' %}
|
||||||
<h6>{% trans "update_restreamconfig_configuration_header" %}</h6>
|
<h6>{% trans "create_new_restream_configuration_header" %}</h6>
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm border-right">
|
<div class="col-sm border-right">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
{% trans "restreamconfig_configuration_text_html" %}
|
{% trans "restream_configuration_text_html" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
<div class="row justify-content-between">
|
<div class="row justify-content-between">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h6>{% trans "restreamconfig_configuration_header" %}</h6>
|
<h6>{% trans "restream_configuration_header" %}</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<a href="{% url 'restream:restreamconfig_create' %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'plus' %} {% trans "create" %}</a>
|
<a href="{% url 'config:restream_create' %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'plus' %} {% trans "create" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,5 +59,5 @@
|
||||||
|
|
||||||
<script src="{% static 'js/vue.min.js' %}"></script>
|
<script src="{% static 'js/vue.min.js' %}"></script>
|
||||||
<script src="{% static 'js/axios.min.js' %}"></script>
|
<script src="{% static 'js/axios.min.js' %}"></script>
|
||||||
<script src="{% static 'js/restreamconfig-list.js' %}"></script>
|
<script src="{% static 'js/restream-list.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -2,7 +2,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap4 %}
|
{% load bootstrap4 %}
|
||||||
{% block 'content' %}
|
{% block 'content' %}
|
||||||
<h6>{% trans "create_new_restreamconfig_configuration_header" %}</h6>
|
<h6>{% trans "update_restream_configuration_header" %}</h6>
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm border-right">
|
<div class="col-sm border-right">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
{% trans "restreamconfig_configuration_text_html" %}
|
{% trans "restream_configuration_text_html" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -14,10 +14,10 @@
|
||||||
{% get_obj_perms user for object as "obj_perms" %}
|
{% get_obj_perms user for object as "obj_perms" %}
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
{% if "change_stream" in obj_perms %}
|
{% if "change_stream" in obj_perms %}
|
||||||
<a href="{% url 'rtmp:stream_change' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'edit' %} {% trans 'change' %}</a>
|
<a href="{% url 'config:stream_change' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'edit' %} {% trans 'change' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if "delete_stream" in obj_perms %}
|
{% if "delete_stream" in obj_perms %}
|
||||||
<a href="{% url 'rtmp:stream_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %} {% trans 'delete' %}</a>
|
<a href="{% url 'config:stream_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %} {% trans 'delete' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,8 +27,6 @@
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-sm-3">{% trans "name" %}</dt>
|
<dt class="col-sm-3">{% trans "name" %}</dt>
|
||||||
<dd class="col-sm-9">{{ object.name }}</dd>
|
<dd class="col-sm-9">{{ object.name }}</dd>
|
||||||
<dt class="col-sm-3">{% trans "application" %}</dt>
|
|
||||||
<dd class="col-sm-9">{{ object.application }}</dd>
|
|
||||||
</dl>
|
</dl>
|
||||||
<h6>{% trans "how_to_configure_your_encoder_header" %}</h6>
|
<h6>{% trans "how_to_configure_your_encoder_header" %}</h6>
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
|
@ -13,7 +13,7 @@
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<a href="{% url 'rtmp:stream_create' %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'plus' %} {% trans "create" %}</a>
|
<a href="{% url 'config:stream_create' %}" type="button" class="btn btn-sm btn-outline-primary">{% fa5_icon 'plus' %} {% trans "create" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
|
@ -1,7 +1,7 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
app_name = 'rtmp'
|
app_name = 'config'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('callback/srs', views.callback_srs, name='callback_srs'),
|
path('callback/srs', views.callback_srs, name='callback_srs'),
|
||||||
|
@ -10,4 +10,9 @@ urlpatterns = [
|
||||||
path('streams/<int:pk>/change', views.StreamChange.as_view(), name='stream_change'),
|
path('streams/<int:pk>/change', views.StreamChange.as_view(), name='stream_change'),
|
||||||
path('streams/<int:pk>/delete', views.StreamDelete.as_view(), name='stream_delete'),
|
path('streams/<int:pk>/delete', views.StreamDelete.as_view(), name='stream_delete'),
|
||||||
path('streams/create', views.StreamCreate.as_view(), name='stream_create'),
|
path('streams/create', views.StreamCreate.as_view(), name='stream_create'),
|
||||||
|
path('restream/', views.RestreamList.as_view(), name='restream_list'),
|
||||||
|
path('restream/<int:pk>/', views.RestreamDetail.as_view(), name='restream_detail'),
|
||||||
|
path('restream/<int:pk>/change', views.RestreamUpdate.as_view(), name='restream_change'),
|
||||||
|
path('restream/<int:pk>/delete', views.RestreamDelete.as_view(), name='restream_delete'),
|
||||||
|
path('restream/create', views.RestreamCreate.as_view(), name='restream_create'),
|
||||||
]
|
]
|
|
@ -7,13 +7,12 @@ from django.urls import reverse_lazy
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.admin.utils import NestedObjects
|
from django.contrib.admin.utils import NestedObjects
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
|
||||||
from django.views.generic import ListView, DetailView, CreateView, DeleteView, UpdateView
|
from django.views.generic import ListView, DetailView, CreateView, DeleteView, UpdateView
|
||||||
from guardian.decorators import permission_required_or_403
|
from guardian.decorators import permission_required_or_403
|
||||||
from guardian.shortcuts import assign_perm
|
from guardian.shortcuts import assign_perm
|
||||||
|
|
||||||
from . import models
|
from . import models, forms
|
||||||
from . import forms
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -40,8 +39,7 @@ def callback_srs(request):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return HttpResponse('1', status=401)
|
return HttpResponse('1', status=401)
|
||||||
try:
|
try:
|
||||||
application = models.Application.objects.get(name=app_name)
|
stream = models.Stream.objects.get(stream=stream_name)
|
||||||
stream = models.Stream.objects.get(stream=stream_name, application=application)
|
|
||||||
|
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
return HttpResponse('1', status=401)
|
return HttpResponse('1', status=401)
|
||||||
|
@ -56,14 +54,14 @@ def callback_srs(request):
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
@method_decorator(permission_required_or_403('rtmp.add_stream'),
|
@method_decorator(permission_required_or_403('config.add_stream'),
|
||||||
name='dispatch')
|
name='dispatch')
|
||||||
class StreamList(ListView):
|
class StreamList(ListView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
@method_decorator(permission_required_or_403('rtmp.view_stream',
|
@method_decorator(permission_required_or_403('config.view_stream',
|
||||||
(models.Stream, 'pk', 'pk')),
|
(models.Stream, 'pk', 'pk')),
|
||||||
name='dispatch')
|
name='dispatch')
|
||||||
class StreamDetail(DetailView):
|
class StreamDetail(DetailView):
|
||||||
|
@ -71,12 +69,11 @@ class StreamDetail(DetailView):
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
@method_decorator(permission_required_or_403('rtmp.change_stream',
|
@method_decorator(permission_required_or_403('config.change_stream',
|
||||||
(models.Stream, 'pk', 'pk')),
|
(models.Stream, 'pk', 'pk')),
|
||||||
name='dispatch')
|
name='dispatch')
|
||||||
class StreamChange(UpdateView):
|
class StreamChange(UpdateView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
form_class = forms.StreamFilteredApplicationForm
|
|
||||||
template_name_suffix = '_update_form'
|
template_name_suffix = '_update_form'
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
|
@ -86,16 +83,11 @@ class StreamChange(UpdateView):
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
@method_decorator(permission_required_or_403('rtmp.add_stream'),
|
@method_decorator(permission_required_or_403('config.add_stream'),
|
||||||
name='dispatch')
|
name='dispatch')
|
||||||
class StreamCreate(CreateView):
|
class StreamCreate(CreateView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
form_class = forms.StreamFilteredApplicationForm
|
fields = ['name']
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
|
||||||
kwargs = super().get_form_kwargs()
|
|
||||||
kwargs['user'] = self.request.user
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
valid = super().form_valid(form)
|
valid = super().form_valid(form)
|
||||||
|
@ -108,12 +100,12 @@ class StreamCreate(CreateView):
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
@method_decorator(permission_required_or_403('rtmp.delete_stream',
|
@method_decorator(permission_required_or_403('config.delete_stream',
|
||||||
(models.Stream, 'pk', 'pk')),
|
(models.Stream, 'pk', 'pk')),
|
||||||
name='dispatch')
|
name='dispatch')
|
||||||
class StreamDelete(DeleteView):
|
class StreamDelete(DeleteView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
success_url = reverse_lazy('rtmp:stream_list')
|
success_url = reverse_lazy('config:stream_list')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
@ -125,3 +117,65 @@ class StreamDelete(DeleteView):
|
||||||
|
|
||||||
print(context['to_delete'])
|
print(context['to_delete'])
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('config.add_restream'),
|
||||||
|
name='dispatch')
|
||||||
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
|
class RestreamList(ListView):
|
||||||
|
model = models.Restream
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('config.view_restream',
|
||||||
|
(models.Restream, 'pk', 'pk')),
|
||||||
|
name='dispatch')
|
||||||
|
class RestreamDetail(DetailView):
|
||||||
|
model = models.Restream
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('config.change_restream',
|
||||||
|
(models.Restream, 'pk', 'pk')),
|
||||||
|
name='dispatch')
|
||||||
|
class RestreamUpdate(UpdateView):
|
||||||
|
model = models.Restream
|
||||||
|
form_class = forms.RestreamFilteredStreamForm
|
||||||
|
template_name_suffix = '_update_form'
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
kwargs = super().get_form_kwargs()
|
||||||
|
kwargs['user'] = self.request.user
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('config.add_restream'),
|
||||||
|
name='dispatch')
|
||||||
|
class RestreamCreate(CreateView):
|
||||||
|
model = models.Restream
|
||||||
|
form_class = forms.RestreamFilteredStreamForm
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
kwargs = super().get_form_kwargs()
|
||||||
|
kwargs['user'] = self.request.user
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
valid = super().form_valid(form)
|
||||||
|
if valid:
|
||||||
|
user = self.request.user
|
||||||
|
assign_perm('view_restream', user, self.object)
|
||||||
|
assign_perm('change_restream', user, self.object)
|
||||||
|
assign_perm('delete_restream', user, self.object)
|
||||||
|
return valid
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('config.delete_restream',
|
||||||
|
(models.Restream, 'pk', 'pk')),
|
||||||
|
name='dispatch')
|
||||||
|
class RestreamDelete(DeleteView):
|
||||||
|
model = models.Restream
|
||||||
|
success_url = reverse_lazy('config:restream_list')
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
||||||
|
|
||||||
PERMISSIONS = [
|
PERMISSIONS = [
|
||||||
'add_stream',
|
'add_stream',
|
||||||
'add_restreamconfig'
|
'add_restream'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
return render(request, 'core/index.html')
|
|
@ -1,3 +0,0 @@
|
||||||
from django.contrib import admin # noqa
|
|
||||||
|
|
||||||
# Register your models here.
|
|
|
@ -1,5 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class PortalConfig(AppConfig):
|
|
||||||
name = 'portal'
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.db import models # noqa
|
|
||||||
|
|
||||||
# Create your models here.
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase # noqa
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -1,6 +0,0 @@
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
|
||||||
# do fancy stuff here maybe
|
|
||||||
return render(request, 'portal/index.html')
|
|
|
@ -30,7 +30,7 @@ CSRF_TRUSTED_ORIGINS = os.environ.get("DJANGO_CSRF_TRUSTED_ORIGINS", default="ht
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_GROUP = 'default'
|
DEFAULT_GROUP = 'default'
|
||||||
DEFAULT_RTMP_APPPLICATION = 'live'
|
GLOBAL_STREAM_NAMESPACE = 'live'
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
|
@ -47,10 +47,8 @@ INSTALLED_APPS = [
|
||||||
'bootstrap4',
|
'bootstrap4',
|
||||||
'fontawesome_5',
|
'fontawesome_5',
|
||||||
'core.apps.CoreConfig',
|
'core.apps.CoreConfig',
|
||||||
'portal.apps.PortalConfig',
|
'config.apps.ConfigConfig',
|
||||||
'rtmp.apps.RtmpConfig',
|
|
||||||
'concierge.apps.ConciergeConfig',
|
'concierge.apps.ConciergeConfig',
|
||||||
'restream.apps.RestreamConfig',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
|
@ -1,18 +1,3 @@
|
||||||
"""portier URL Configuration
|
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
|
||||||
https://docs.djangoproject.com/en/3.0/topics/http/urls/
|
|
||||||
Examples:
|
|
||||||
Function views
|
|
||||||
1. Add an import: from my_app import views
|
|
||||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
|
||||||
Class-based views
|
|
||||||
1. Add an import: from other_app.views import Home
|
|
||||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
|
||||||
Including another URLconf
|
|
||||||
1. Import the include() function: from django.urls import include, path
|
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
|
||||||
"""
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
|
@ -21,9 +6,8 @@ urlpatterns = [
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path('i18n/', include('django.conf.urls.i18n')),
|
path('i18n/', include('django.conf.urls.i18n')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('rtmp/', include('rtmp.urls')),
|
path('config/', include('config.urls')),
|
||||||
path('restream/', include('restream.urls')),
|
|
||||||
path('concierge/', include('concierge.urls')),
|
path('concierge/', include('concierge.urls')),
|
||||||
path('api/v1/', include('restapi.urls')),
|
path('api/v1/', include('restapi.urls')),
|
||||||
path('', include('portal.urls')),
|
path('', include('core.urls')),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
from .views import ApplicationViewSet, StreamViewSet, RestreamConfigViewSet
|
from .views import StreamViewSet, RestreamViewSet
|
||||||
|
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register(r'applications', ApplicationViewSet)
|
|
||||||
router.register(r'streams', StreamViewSet)
|
router.register(r'streams', StreamViewSet)
|
||||||
router.register(r'restreamconfigs', RestreamConfigViewSet)
|
router.register(r'restreams', RestreamViewSet)
|
||||||
|
|
||||||
app_name = 'restapi'
|
app_name = 'restapi'
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,7 @@
|
||||||
from rest_framework_guardian.serializers import ObjectPermissionsAssignmentMixin
|
from rest_framework_guardian.serializers import ObjectPermissionsAssignmentMixin
|
||||||
from rest_framework import serializers, viewsets
|
from rest_framework import serializers, viewsets
|
||||||
from rest_framework_guardian import filters
|
from rest_framework_guardian import filters
|
||||||
from rtmp.models import Application, Stream
|
from config.models import Stream, Restream
|
||||||
from restream.models import RestreamConfig
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationSerializer(serializers.HyperlinkedModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Application
|
|
||||||
fields = ['id', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationViewSet(viewsets.ReadOnlyModelViewSet):
|
|
||||||
queryset = Application.objects.all()
|
|
||||||
serializer_class = ApplicationSerializer
|
|
||||||
filter_backends = [filters.ObjectPermissionsFilter]
|
|
||||||
|
|
||||||
|
|
||||||
class StreamSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
|
class StreamSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
|
||||||
|
@ -38,9 +25,9 @@ class StreamViewSet(viewsets.ModelViewSet):
|
||||||
filter_backends = [filters.ObjectPermissionsFilter]
|
filter_backends = [filters.ObjectPermissionsFilter]
|
||||||
|
|
||||||
|
|
||||||
class RestreamConfigSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
|
class RestreamSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RestreamConfig
|
model = Restream
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
def get_permissions_map(self, created):
|
def get_permissions_map(self, created):
|
||||||
|
@ -53,12 +40,12 @@ class RestreamConfigSerializer(ObjectPermissionsAssignmentMixin, serializers.Mod
|
||||||
|
|
||||||
def validate_stream(self, value):
|
def validate_stream(self, value):
|
||||||
request = self.context['request']
|
request = self.context['request']
|
||||||
if not request.user.has_perm('rtmp.view_stream', value):
|
if not request.user.has_perm('config.view_stream', value):
|
||||||
raise serializers.ValidationError('Access to stream is not authorized')
|
raise serializers.ValidationError('Access to stream is not authorized')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class RestreamConfigViewSet(viewsets.ModelViewSet):
|
class RestreamViewSet(viewsets.ModelViewSet):
|
||||||
queryset = RestreamConfig.objects.all()
|
queryset = Restream.objects.all()
|
||||||
serializer_class = RestreamConfigSerializer
|
serializer_class = RestreamSerializer
|
||||||
filter_backends = [filters.ObjectPermissionsFilter]
|
filter_backends = [filters.ObjectPermissionsFilter]
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
from guardian.admin import GuardedModelAdmin
|
|
||||||
from .models import RestreamConfig
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(RestreamConfig)
|
|
||||||
class RestreamConfigAdmin(GuardedModelAdmin):
|
|
||||||
fields = ['name', 'active', 'stream', 'format', 'target']
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class RestreamConfig(AppConfig):
|
|
||||||
name = 'restream'
|
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
import restream.signals # noqa
|
|
|
@ -1,26 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-23 19:04
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('rtmp', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='RestreamConfig',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('target', models.CharField(max_length=500)),
|
|
||||||
('name', models.CharField(max_length=100)),
|
|
||||||
('active', models.BooleanField()),
|
|
||||||
('stream', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rtmp.Stream')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,39 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-05-01 13:02
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('rtmp', '0004_auto_20200501_1302'),
|
|
||||||
('restream', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='restreamconfig',
|
|
||||||
options={'verbose_name': 'restreamconfig_verbose_name', 'verbose_name_plural': 'restreamconfig_verbose_name_plural'},
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='restreamconfig',
|
|
||||||
name='active',
|
|
||||||
field=models.BooleanField(help_text='restreamconfig_activate_help'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='restreamconfig',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(help_text='restreamconfig_name_help', max_length=100),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='restreamconfig',
|
|
||||||
name='stream',
|
|
||||||
field=models.ForeignKey(help_text='restreamconfig_stream_help', on_delete=django.db.models.deletion.CASCADE, to='rtmp.Stream'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='restreamconfig',
|
|
||||||
name='target',
|
|
||||||
field=models.CharField(help_text='restreamconfig_target_help', max_length=500),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Generated by Django 3.1.13 on 2021-12-14 17:35
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('restream', '0002_auto_20200501_1302'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='restreamconfig',
|
|
||||||
name='format',
|
|
||||||
field=models.CharField(choices=[('flv', 'flv (RTMP)'), ('mpegts', 'mpegts (SRT)')], default='flv', help_text='restreamconfig_format_help', max_length=6),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,46 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.db.models.signals import pre_delete
|
|
||||||
from portier.common import handlers
|
|
||||||
import json
|
|
||||||
|
|
||||||
from rtmp.models import Stream
|
|
||||||
|
|
||||||
|
|
||||||
class RestreamConfig(models.Model):
|
|
||||||
FORMATS = (
|
|
||||||
('flv', 'flv (RTMP)'),
|
|
||||||
('mpegts', 'mpegts (SRT)'),
|
|
||||||
)
|
|
||||||
stream = models.ForeignKey(Stream, on_delete=models.CASCADE, help_text=_('restreamconfig_stream_help'))
|
|
||||||
target = models.CharField(max_length=500, help_text=_('restreamconfig_target_help'))
|
|
||||||
name = models.CharField(max_length=100, help_text=_('restreamconfig_name_help'))
|
|
||||||
active = models.BooleanField(help_text=_('restreamconfig_activate_help'))
|
|
||||||
format = models.CharField(max_length=6, choices=FORMATS, default='flv', help_text=_('restreamconfig_format_help'))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('restreamconfig_verbose_name')
|
|
||||||
verbose_name_plural = _('restreamconfig_verbose_name_plural')
|
|
||||||
|
|
||||||
def class_name(self):
|
|
||||||
return _('restreamconfig_class_name')
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('restream:restreamconfig_detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{} to {}'.format(self.stream, self.name)
|
|
||||||
|
|
||||||
def get_json_config(self):
|
|
||||||
config = {
|
|
||||||
'name': self.name,
|
|
||||||
'app': self.stream.application.name,
|
|
||||||
'stream': str(self.stream.stream),
|
|
||||||
'target': self.target,
|
|
||||||
'format': self.format
|
|
||||||
}
|
|
||||||
return json.dumps(config)
|
|
||||||
|
|
||||||
|
|
||||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=RestreamConfig)
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase # noqa
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -1,12 +0,0 @@
|
||||||
from django.urls import path
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
app_name = 'restream'
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('restreamconfig/', views.RestreamConfigList.as_view(), name='restreamconfig_list'),
|
|
||||||
path('restreamconfig/<int:pk>/', views.RestreamConfigDetail.as_view(), name='restreamconfig_detail'),
|
|
||||||
path('restreamconfig/<int:pk>/change', views.RestreamConfigChange.as_view(), name='restreamconfig_change'),
|
|
||||||
path('restreamconfig/<int:pk>/delete', views.RestreamConfigDelete.as_view(), name='restreamconfig_delete'),
|
|
||||||
path('restreamconfig/create', views.RestreamConfigCreate.as_view(), name='restreamconfig_create'),
|
|
||||||
]
|
|
|
@ -1,72 +0,0 @@
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
|
||||||
from django.utils.decorators import method_decorator
|
|
||||||
from django.views.generic import ListView, DetailView, CreateView, DeleteView, UpdateView
|
|
||||||
from guardian.decorators import permission_required_or_403
|
|
||||||
from guardian.shortcuts import assign_perm
|
|
||||||
|
|
||||||
from . import models
|
|
||||||
from . import forms
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
|
||||||
@method_decorator(permission_required_or_403('restream.add_restreamconfig'),
|
|
||||||
name='dispatch')
|
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
|
||||||
class RestreamConfigList(ListView):
|
|
||||||
model = models.RestreamConfig
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
|
||||||
@method_decorator(permission_required_or_403('restream.view_restreamconfig',
|
|
||||||
(models.RestreamConfig, 'pk', 'pk')),
|
|
||||||
name='dispatch')
|
|
||||||
class RestreamConfigDetail(DetailView):
|
|
||||||
model = models.RestreamConfig
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
|
||||||
@method_decorator(permission_required_or_403('restream.change_restreamconfig',
|
|
||||||
(models.RestreamConfig, 'pk', 'pk')),
|
|
||||||
name='dispatch')
|
|
||||||
class RestreamConfigChange(UpdateView):
|
|
||||||
model = models.RestreamConfig
|
|
||||||
form_class = forms.RestreamConfigFilteredStreamForm
|
|
||||||
template_name_suffix = '_update_form'
|
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
|
||||||
kwargs = super().get_form_kwargs()
|
|
||||||
kwargs['user'] = self.request.user
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
|
||||||
@method_decorator(permission_required_or_403('restream.add_restreamconfig'),
|
|
||||||
name='dispatch')
|
|
||||||
class RestreamConfigCreate(CreateView):
|
|
||||||
model = models.RestreamConfig
|
|
||||||
form_class = forms.RestreamConfigFilteredStreamForm
|
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
|
||||||
kwargs = super().get_form_kwargs()
|
|
||||||
kwargs['user'] = self.request.user
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
valid = super().form_valid(form)
|
|
||||||
if valid:
|
|
||||||
user = self.request.user
|
|
||||||
assign_perm('view_restreamconfig', user, self.object)
|
|
||||||
assign_perm('change_restreamconfig', user, self.object)
|
|
||||||
assign_perm('delete_restreamconfig', user, self.object)
|
|
||||||
return valid
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
|
||||||
@method_decorator(permission_required_or_403('restream.delete_restreamconfig',
|
|
||||||
(models.RestreamConfig, 'pk', 'pk')),
|
|
||||||
name='dispatch')
|
|
||||||
class RestreamConfigDelete(DeleteView):
|
|
||||||
model = models.RestreamConfig
|
|
||||||
success_url = reverse_lazy('restream:restreamconfig_list')
|
|
|
@ -1,15 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
from guardian.admin import GuardedModelAdmin
|
|
||||||
from .models import Application, Stream
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Application)
|
|
||||||
class ApplicationAdmin(GuardedModelAdmin):
|
|
||||||
fields = ['name']
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Stream)
|
|
||||||
class StreamAdmin(GuardedModelAdmin):
|
|
||||||
fields = ['application', 'stream', 'name', 'publish_counter']
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class RtmpConfig(AppConfig):
|
|
||||||
name = 'rtmp'
|
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
import rtmp.signals # noqa
|
|
|
@ -1,16 +0,0 @@
|
||||||
from django.forms import ModelForm
|
|
||||||
from guardian.shortcuts import get_objects_for_user
|
|
||||||
from . import models
|
|
||||||
|
|
||||||
|
|
||||||
class StreamFilteredApplicationForm(ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = models.Stream
|
|
||||||
fields = ['name', 'application']
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
user = kwargs.pop('user', None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# limit the stream selection to user-accessible streams
|
|
||||||
self.fields['application'].queryset = get_objects_for_user(user, 'rtmp.view_application')
|
|
|
@ -1,16 +0,0 @@
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.conf import settings
|
|
||||||
from guardian.shortcuts import assign_perm
|
|
||||||
|
|
||||||
from rtmp import models
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = 'Creates a default RTMP application that is available to all users in the default group'
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
default_group, _ = Group.objects.get_or_create(name=settings.DEFAULT_GROUP)
|
|
||||||
default_app, _ = models.Application.objects.get_or_create(name=settings.DEFAULT_RTMP_APPPLICATION)
|
|
||||||
|
|
||||||
assign_perm('view_application', default_group, default_app)
|
|
|
@ -1,32 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-23 19:04
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Application',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(help_text='rtmp_application_name', max_length=100, unique=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Stream',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('stream', models.CharField(default=uuid.uuid4, max_length=64, unique=True)),
|
|
||||||
('name', models.CharField(max_length=100)),
|
|
||||||
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rtmp.Application')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-26 17:25
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('rtmp', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='stream',
|
|
||||||
name='publish_counter',
|
|
||||||
field=models.PositiveIntegerField(default=0),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,19 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-04-26 18:34
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('rtmp', '0002_stream_publish_counter'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='stream',
|
|
||||||
name='stream',
|
|
||||||
field=models.UUIDField(default=uuid.uuid4, unique=True),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,34 +0,0 @@
|
||||||
# Generated by Django 3.0.5 on 2020-05-01 13:02
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('rtmp', '0003_auto_20200426_1834'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='application',
|
|
||||||
options={'verbose_name': 'application_verbose_name', 'verbose_name_plural': 'application_verbose_name_plural'},
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='stream',
|
|
||||||
name='application',
|
|
||||||
field=models.ForeignKey(help_text='stream_application_help', on_delete=django.db.models.deletion.CASCADE, to='rtmp.Application'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='stream',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(help_text='stream_name_help', max_length=100),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='stream',
|
|
||||||
name='stream',
|
|
||||||
field=models.UUIDField(default=uuid.uuid4, help_text='stream_stream_help', unique=True),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,39 +0,0 @@
|
||||||
# Generated by Django 3.0.6 on 2020-05-31 09:51
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('rtmp', '0004_auto_20200501_1302'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='application',
|
|
||||||
options={'verbose_name': 'RTMP application', 'verbose_name_plural': 'RTMP applications'},
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='application',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(help_text='RTMP application name', max_length=100, unique=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='stream',
|
|
||||||
name='application',
|
|
||||||
field=models.ForeignKey(help_text='Application which the stream is assigned to', on_delete=django.db.models.deletion.CASCADE, to='rtmp.Application'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='stream',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(help_text='Name for this stream', max_length=100),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='stream',
|
|
||||||
name='stream',
|
|
||||||
field=models.UUIDField(default=uuid.uuid4, help_text='Stream ID for this stream', unique=True),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase # noqa
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -25,7 +25,6 @@ migrate() {
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
python manage.py createdefaultgroup
|
python manage.py createdefaultgroup
|
||||||
python manage.py createdefaultapplication
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_for_redis
|
wait_for_redis
|
||||||
|
|
|
@ -13,7 +13,7 @@ var app = new Vue({
|
||||||
},
|
},
|
||||||
toggleActive(cfg) {
|
toggleActive(cfg) {
|
||||||
axios
|
axios
|
||||||
.patch('/api/v1/restreamconfigs/' + cfg.id + '/', { active: !cfg.active })
|
.patch('/api/v1/restream/' + cfg.id + '/', { active: !cfg.active })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
i = this.cfgs.findIndex((obj => obj.id == cfg.id))
|
i = this.cfgs.findIndex((obj => obj.id == cfg.id))
|
||||||
Vue.set(this.cfgs, i, response.data)
|
Vue.set(this.cfgs, i, response.data)
|
||||||
|
@ -22,7 +22,7 @@ var app = new Vue({
|
||||||
},
|
},
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios
|
axios
|
||||||
.get('/api/v1/restreamconfigs/')
|
.get('/api/v1/restreams/')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.cfgs = response.data
|
this.cfgs = response.data
|
||||||
this.isLoading = false
|
this.isLoading = false
|
|
@ -39,8 +39,8 @@
|
||||||
{% trans "navbar_streaming" %}
|
{% trans "navbar_streaming" %}
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarStreamingDropdown">
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarStreamingDropdown">
|
||||||
<a class="dropdown-item{% if not perms.rtmp.add_stream %} disabled{% endif %}" href="{% url 'rtmp:stream_list' %}">{% trans "navbar_configuration_streams" %}</a>
|
<a class="dropdown-item{% if not perms.config.add_stream %} disabled{% endif %}" href="{% url 'config:stream_list' %}">{% trans "navbar_configuration_streams" %}</a>
|
||||||
<a class="dropdown-item{% if not perms.restream.add_restreamconfig %} disabled{% endif %}" href="{% url 'restream:restreamconfig_list' %}">{% trans "navbar_configuration_restreams" %}</a>
|
<a class="dropdown-item{% if not perms.config.add_restream %} disabled{% endif %}" href="{% url 'config:restream_list' %}">{% trans "navbar_configuration_restreams" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
Loading…
Reference in New Issue