wip
This commit is contained in:
		
							parent
							
								
									d2f980c318
								
							
						
					
					
						commit
						0a857a194c
					
				| 
						 | 
					@ -1,23 +1,38 @@
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from guardian.admin import GuardedModelAdmin
 | 
					from guardian.admin import GuardedModelAdmin
 | 
				
			||||||
from config.models import Stream, Restream, Pull, SRSNode, SRSStreamInstance
 | 
					from config.models import TranscodingProfile, Stream, Restream, Pull, Recorder, LocalRecordingStorage, S3RecordingStorage, SRSNode, SRSStreamInstance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(LocalRecordingStorage)
 | 
				
			||||||
 | 
					class LocalRecordingStorageAdmin(GuardedModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(S3RecordingStorage)
 | 
				
			||||||
 | 
					class S3RecordingStorageAdmin(GuardedModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Recorder)
 | 
				
			||||||
 | 
					class RecorderAdmin(GuardedModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(TranscodingProfile)
 | 
				
			||||||
 | 
					class TranscodingProfileAdmin(GuardedModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@admin.register(Stream)
 | 
					@admin.register(Stream)
 | 
				
			||||||
class StreamAdmin(GuardedModelAdmin):
 | 
					class StreamAdmin(GuardedModelAdmin):
 | 
				
			||||||
    fields = ['stream', 'name', 'publish_counter']
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@admin.register(Restream)
 | 
					@admin.register(Restream)
 | 
				
			||||||
class RestreamAdmin(GuardedModelAdmin):
 | 
					class RestreamAdmin(GuardedModelAdmin):
 | 
				
			||||||
    fields = ['name', 'active', 'stream', 'format', 'target']
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@admin.register(Pull)
 | 
					@admin.register(Pull)
 | 
				
			||||||
class PullAdmin(GuardedModelAdmin):
 | 
					class PullAdmin(GuardedModelAdmin):
 | 
				
			||||||
    fields = ['name', 'active', 'stream', 'source']
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@admin.register(SRSNode)
 | 
					@admin.register(SRSNode)
 | 
				
			||||||
class SRSNodeAdmin(GuardedModelAdmin):
 | 
					class SRSNodeAdmin(GuardedModelAdmin):
 | 
				
			||||||
    fields = ['name', 'api_base', 'rtmp_base', 'active']
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@admin.register(SRSStreamInstance)
 | 
					@admin.register(SRSStreamInstance)
 | 
				
			||||||
class SRSStreamInstanceAdmin(GuardedModelAdmin):
 | 
					class SRSStreamInstanceAdmin(GuardedModelAdmin):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					from . import pull, recorder, restream, stream
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,20 @@ class StreamCreate(ModelSchema):
 | 
				
			||||||
    extra = "forbid"
 | 
					    extra = "forbid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LocalRecordingStorage(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.LocalRecordingStorage
 | 
				
			||||||
 | 
					    fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LocalRecordingStoragePatch(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.LocalRecordingStorage
 | 
				
			||||||
 | 
					    exclude = ["id"]
 | 
				
			||||||
 | 
					    fields_optional = "__all__"
 | 
				
			||||||
 | 
					    extra = "forbid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@router.get('/streams', response=List[Stream])
 | 
					@router.get('/streams', response=List[Stream])
 | 
				
			||||||
def list_streams(request):
 | 
					def list_streams(request):
 | 
				
			||||||
  return get_objects_for_user(request.user, 'view_stream', models.Stream.objects.all())
 | 
					  return get_objects_for_user(request.user, 'view_stream', models.Stream.objects.all())
 | 
				
			||||||
| 
						 | 
					@ -123,7 +137,7 @@ def create_restream(request, payload: Restream):
 | 
				
			||||||
@router.get('/restreams/{id}', response=Restream)
 | 
					@router.get('/restreams/{id}', response=Restream)
 | 
				
			||||||
def get_restream(request, id: int):
 | 
					def get_restream(request, id: int):
 | 
				
			||||||
  restream = get_object_or_404(models.Restream, id=id)
 | 
					  restream = get_object_or_404(models.Restream, id=id)
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
  if not request.user.has_perm('view_restream', restream):
 | 
					  if not request.user.has_perm('view_restream', restream):
 | 
				
			||||||
    raise HttpError(401, "unauthorized")
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,7 +153,7 @@ def update_restream(request, id: int, payload: RestreamPatch):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if payload.stream:
 | 
					  if payload.stream:
 | 
				
			||||||
    payload.stream = get_object_or_404(models.Stream, id=payload.stream)
 | 
					    payload.stream = get_object_or_404(models.Stream, id=payload.stream)
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    if not request.user.has_perm('view_stream', payload.stream):
 | 
					    if not request.user.has_perm('view_stream', payload.stream):
 | 
				
			||||||
      raise HttpError(401, "unauthorized")
 | 
					      raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -171,20 +185,20 @@ def create_pull(request, payload: Pull):
 | 
				
			||||||
    raise HttpError(401, "unauthorized")
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pull = models.Pull.objects.create(**payload.dict())
 | 
					  pull = models.Pull.objects.create(**payload.dict())
 | 
				
			||||||
  assign_perm(  'view_pull', request.user, Pull)
 | 
					  assign_perm(  'view_pull', request.user, pull)
 | 
				
			||||||
  assign_perm('change_pull', request.user, Pull)
 | 
					  assign_perm('change_pull', request.user, pull)
 | 
				
			||||||
  assign_perm('delete_pull', request.user, Pull)
 | 
					  assign_perm('delete_pull', request.user, pull)
 | 
				
			||||||
  return pull 
 | 
					  return pull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@router.get('/pulls/{id}', response=Pull)
 | 
					@router.get('/pulls/{id}', response=Pull)
 | 
				
			||||||
def get_pull(request, id: int):
 | 
					def get_pull(request, id: int):
 | 
				
			||||||
  pull = get_object_or_404(models.Pull, id=id)
 | 
					  pull = get_object_or_404(models.Pull, id=id)
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
  if not request.user.has_perm('view_pull', pull):
 | 
					  if not request.user.has_perm('view_pull', pull):
 | 
				
			||||||
    raise HttpError(401, "unauthorized")
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return pull 
 | 
					  return pull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@router.patch('/pulls/{id}', response=Pull)
 | 
					@router.patch('/pulls/{id}', response=Pull)
 | 
				
			||||||
| 
						 | 
					@ -196,7 +210,7 @@ def patch_pull(request, id: int, payload: PullPatch):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if payload.stream:
 | 
					  if payload.stream:
 | 
				
			||||||
    payload.stream = get_object_or_404(models.Stream, id=payload.stream)
 | 
					    payload.stream = get_object_or_404(models.Stream, id=payload.stream)
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    if not request.user.has_perm('view_stream', payload.stream):
 | 
					    if not request.user.has_perm('view_stream', payload.stream):
 | 
				
			||||||
      raise HttpError(401, "unauthorized")
 | 
					      raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -213,4 +227,45 @@ def delete_pull(request, id: int):
 | 
				
			||||||
  if not request.user.has_perm('delete_pull', pull):
 | 
					  if not request.user.has_perm('delete_pull', pull):
 | 
				
			||||||
    raise HttpError(401, "unauthorized")
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pull.delete()
 | 
					  pull.delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/recording/storage/local', response=List[LocalRecordingStorage])
 | 
				
			||||||
 | 
					def list_local_recording_storage(request):
 | 
				
			||||||
 | 
					  return get_objects_for_user(request.user, 'view_localrecordingstorage', models.LocalRecordingStorage.objects.all())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/recording/storage/local/{id}', response=LocalRecordingStorage)
 | 
				
			||||||
 | 
					def get_local_recording_storage(request, id: int):
 | 
				
			||||||
 | 
					  obj = get_object_or_404(models.LocalRecordingStorage, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('view_localrecordingstorage', obj):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post('/recording/storage/local', response=LocalRecordingStorage)
 | 
				
			||||||
 | 
					def create_local_recording_storage(request, payload: LocalRecordingStorage):
 | 
				
			||||||
 | 
					  obj = models.LocalRecordingStorage.objects.create(**payload.dict())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  assign_perm(  'view_localrecordingstorage', request.user, obj)
 | 
				
			||||||
 | 
					  assign_perm('change_localrecordingstorage', request.user, obj)
 | 
				
			||||||
 | 
					  assign_perm('delete_localrecordingstorage', request.user, obj)
 | 
				
			||||||
 | 
					  return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.patch('/recording/storage/local/{id}', response=LocalRecordingStorage)
 | 
				
			||||||
 | 
					def patch_local_recording_storage(request, id: int, payload: LocalRecordingStoragePatch):
 | 
				
			||||||
 | 
					  obj = get_object_or_404(models.LocalRecordingStorage, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('change_localrecordingstorage', obj):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for key, value in payload.dict(exclude_unset=True).items():
 | 
				
			||||||
 | 
					    setattr(obj, key, value)
 | 
				
			||||||
 | 
					  obj.save()
 | 
				
			||||||
 | 
					  return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.delete('/recording/storage/local/{id}', response=None)
 | 
				
			||||||
 | 
					def delete_local_recording_storage(request, id: int):
 | 
				
			||||||
 | 
					  obj = get_object_or_404(models.LocalRecordingStorage, id=id)
 | 
				
			||||||
 | 
					  if not request.user.has_perm('delete_localrecordingstorage', obj):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					  obj.delete()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					from ninja import Router, ModelSchema, Schema
 | 
				
			||||||
 | 
					from ninja.errors import HttpError
 | 
				
			||||||
 | 
					from config import models
 | 
				
			||||||
 | 
					from typing import List
 | 
				
			||||||
 | 
					from guardian.shortcuts import get_objects_for_user, assign_perm
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router = Router()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Pull(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.Pull
 | 
				
			||||||
 | 
					    fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PullPatch(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.Pull
 | 
				
			||||||
 | 
					    exclude = ["id"]
 | 
				
			||||||
 | 
					    fields_optional = "__all__"
 | 
				
			||||||
 | 
					    extra = "forbid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/pulls', response=List[Pull])
 | 
				
			||||||
 | 
					def list_pulls(request):
 | 
				
			||||||
 | 
					  return get_objects_for_user(request.user, 'view_pull', models.Pull.objects.all())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post('/pulls', response=Pull)
 | 
				
			||||||
 | 
					def create_pull(request, payload: Pull):
 | 
				
			||||||
 | 
					  if not request.user.has_perm('view_stream', payload.stream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pull = models.Pull.objects.create(**payload.dict())
 | 
				
			||||||
 | 
					  assign_perm(  'view_pull', request.user, pull)
 | 
				
			||||||
 | 
					  assign_perm('change_pull', request.user, pull)
 | 
				
			||||||
 | 
					  assign_perm('delete_pull', request.user, pull)
 | 
				
			||||||
 | 
					  return pull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/pulls/{id}', response=Pull)
 | 
				
			||||||
 | 
					def get_pull(request, id: int):
 | 
				
			||||||
 | 
					  pull = get_object_or_404(models.Pull, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('view_pull', pull):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return pull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.patch('/pulls/{id}', response=Pull)
 | 
				
			||||||
 | 
					def patch_pull(request, id: int, payload: PullPatch):
 | 
				
			||||||
 | 
					  pull = get_object_or_404(models.Pull, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('change_pull', pull):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if payload.stream:
 | 
				
			||||||
 | 
					    payload.stream = get_object_or_404(models.Stream, id=payload.stream)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not request.user.has_perm('view_stream', payload.stream):
 | 
				
			||||||
 | 
					      raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for key, value in payload.dict(exclude_unset=True).items():
 | 
				
			||||||
 | 
					    setattr(pull, key, value)
 | 
				
			||||||
 | 
					  pull.save()
 | 
				
			||||||
 | 
					  return pull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.delete('/pulls/{id}', response=None)
 | 
				
			||||||
 | 
					def delete_pull(request, id: int):
 | 
				
			||||||
 | 
					  pull = get_object_or_404(models.Pull, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('delete_pull', pull):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pull.delete()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,65 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ninja import Router, ModelSchema, Schema
 | 
				
			||||||
 | 
					from ninja.errors import HttpError
 | 
				
			||||||
 | 
					from config import models
 | 
				
			||||||
 | 
					from typing import List
 | 
				
			||||||
 | 
					from guardian.shortcuts import get_objects_for_user, assign_perm
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router = Router()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LocalRecordingStorage(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.LocalRecordingStorage
 | 
				
			||||||
 | 
					    fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LocalRecordingStoragePatch(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.LocalRecordingStorage
 | 
				
			||||||
 | 
					    exclude = ["id"]
 | 
				
			||||||
 | 
					    fields_optional = "__all__"
 | 
				
			||||||
 | 
					    extra = "forbid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/storage/local', response=List[LocalRecordingStorage])
 | 
				
			||||||
 | 
					def list_local_recording_storage(request):
 | 
				
			||||||
 | 
					  return get_objects_for_user(request.user, 'view_localrecordingstorage', models.LocalRecordingStorage.objects.all())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/storage/local/{id}', response=LocalRecordingStorage)
 | 
				
			||||||
 | 
					def get_local_recording_storage(request, id: int):
 | 
				
			||||||
 | 
					  obj = get_object_or_404(models.LocalRecordingStorage, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('view_localrecordingstorage', obj):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post('/storage/local', response=LocalRecordingStorage)
 | 
				
			||||||
 | 
					def create_local_recording_storage(request, payload: LocalRecordingStorage):
 | 
				
			||||||
 | 
					  obj = models.LocalRecordingStorage.objects.create(**payload.dict())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  assign_perm(  'view_localrecordingstorage', request.user, obj)
 | 
				
			||||||
 | 
					  assign_perm('change_localrecordingstorage', request.user, obj)
 | 
				
			||||||
 | 
					  assign_perm('delete_localrecordingstorage', request.user, obj)
 | 
				
			||||||
 | 
					  return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.patch('/storage/local/{id}', response=LocalRecordingStorage)
 | 
				
			||||||
 | 
					def patch_local_recording_storage(request, id: int, payload: LocalRecordingStoragePatch):
 | 
				
			||||||
 | 
					  obj = get_object_or_404(models.LocalRecordingStorage, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('change_localrecordingstorage', obj):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for key, value in payload.dict(exclude_unset=True).items():
 | 
				
			||||||
 | 
					    setattr(obj, key, value)
 | 
				
			||||||
 | 
					  obj.save()
 | 
				
			||||||
 | 
					  return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.delete('/storage/local/{id}', response=None)
 | 
				
			||||||
 | 
					def delete_local_recording_storage(request, id: int):
 | 
				
			||||||
 | 
					  obj = get_object_or_404(models.LocalRecordingStorage, id=id)
 | 
				
			||||||
 | 
					  if not request.user.has_perm('delete_localrecordingstorage', obj):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					  obj.delete()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,78 @@
 | 
				
			||||||
 | 
					from ninja import Router, ModelSchema, Schema
 | 
				
			||||||
 | 
					from ninja.errors import HttpError
 | 
				
			||||||
 | 
					from config import models
 | 
				
			||||||
 | 
					from typing import List
 | 
				
			||||||
 | 
					from guardian.shortcuts import get_objects_for_user, assign_perm
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router = Router()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Restream(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.Restream
 | 
				
			||||||
 | 
					    fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RestreamPatch(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.Restream
 | 
				
			||||||
 | 
					    exclude = ["id"]
 | 
				
			||||||
 | 
					    fields_optional = "__all__"
 | 
				
			||||||
 | 
					    extra = "forbid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/restreams', response=List[Restream])
 | 
				
			||||||
 | 
					def list_restreams(request):
 | 
				
			||||||
 | 
					  return get_objects_for_user(request.user, 'view_restream', models.Restream.objects.all())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post('/restreams', response=Restream)
 | 
				
			||||||
 | 
					def create_restream(request, payload: Restream):
 | 
				
			||||||
 | 
					  if not request.user.has_perm('view_stream', payload.stream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  restream = models.Restream.objects.create(**payload.dict())
 | 
				
			||||||
 | 
					  assign_perm('view_restream', request.user, restream)
 | 
				
			||||||
 | 
					  assign_perm('change_restream', request.user, restream)
 | 
				
			||||||
 | 
					  assign_perm('delete_restream', request.user, restream)
 | 
				
			||||||
 | 
					  return restream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/restreams/{id}', response=Restream)
 | 
				
			||||||
 | 
					def get_restream(request, id: int):
 | 
				
			||||||
 | 
					  restream = get_object_or_404(models.Restream, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('view_restream', restream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return restream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.patch('/restreams/{id}', response=Restream)
 | 
				
			||||||
 | 
					def update_restream(request, id: int, payload: RestreamPatch):
 | 
				
			||||||
 | 
					  restream = get_object_or_404(models.Restream, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('change_restream', restream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if payload.stream:
 | 
				
			||||||
 | 
					    payload.stream = get_object_or_404(models.Stream, id=payload.stream)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not request.user.has_perm('view_stream', payload.stream):
 | 
				
			||||||
 | 
					      raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for key, value in payload.dict(exclude_unset=True).items():
 | 
				
			||||||
 | 
					    setattr(restream, key, value)
 | 
				
			||||||
 | 
					  restream.save()
 | 
				
			||||||
 | 
					  return restream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.delete('/restreams/{id}', response=None)
 | 
				
			||||||
 | 
					def delete_restream(request, id: int):
 | 
				
			||||||
 | 
					  restream = get_object_or_404(models.Restream, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('delete_restream', restream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  restream.delete()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					from ninja import Router, ModelSchema, Schema
 | 
				
			||||||
 | 
					from ninja.errors import HttpError
 | 
				
			||||||
 | 
					from config import models
 | 
				
			||||||
 | 
					from typing import List
 | 
				
			||||||
 | 
					from guardian.shortcuts import get_objects_for_user, assign_perm
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router = Router()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Stream(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.Stream
 | 
				
			||||||
 | 
					    fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StreamPatch(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.Stream
 | 
				
			||||||
 | 
					    fields = ["name"]
 | 
				
			||||||
 | 
					    fields_optional = "__all__"
 | 
				
			||||||
 | 
					    extra = "forbid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StreamCreate(ModelSchema):
 | 
				
			||||||
 | 
					  class Meta:
 | 
				
			||||||
 | 
					    model = models.Stream
 | 
				
			||||||
 | 
					    fields = ["name"]
 | 
				
			||||||
 | 
					    extra = "forbid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/streams', response=List[Stream])
 | 
				
			||||||
 | 
					def list_streams(request):
 | 
				
			||||||
 | 
					  return get_objects_for_user(request.user, 'view_stream', models.Stream.objects.all())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post('/streams', response=Stream)
 | 
				
			||||||
 | 
					def create_stream(request, payload: StreamCreate):
 | 
				
			||||||
 | 
					  stream = models.Stream.objects.create(**payload.dict())
 | 
				
			||||||
 | 
					  assign_perm('view_stream', request.user, stream)
 | 
				
			||||||
 | 
					  assign_perm('change_stream', request.user, stream)
 | 
				
			||||||
 | 
					  assign_perm('delete_stream', request.user, stream)
 | 
				
			||||||
 | 
					  return stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.get('/streams/{id}', response=Stream)
 | 
				
			||||||
 | 
					def get_stream(request, id: int):
 | 
				
			||||||
 | 
					  stream = get_object_or_404(models.Stream, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('view_stream', stream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					  return stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.patch('/streams/{id}', response=Stream)
 | 
				
			||||||
 | 
					def update_stream(request, id: int, payload: StreamPatch):
 | 
				
			||||||
 | 
					  stream = get_object_or_404(models.Stream, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('change_stream', stream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  stream.name = payload.name
 | 
				
			||||||
 | 
					  stream.save()
 | 
				
			||||||
 | 
					  return stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.delete('/streams/{id}', response=None)
 | 
				
			||||||
 | 
					def delete_stream(request, id: int):
 | 
				
			||||||
 | 
					  stream = get_object_or_404(models.Stream, id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if not request.user.has_perm('delete_stream', stream):
 | 
				
			||||||
 | 
					    raise HttpError(401, "unauthorized")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  stream.delete()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					# Generated by Django 5.0.2 on 2024-04-01 18:42
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import django.db.models.deletion
 | 
				
			||||||
 | 
					import uuid
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('config', '0004_alter_pull_source_alter_restream_target'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='TranscodingProfile',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(help_text='transcodingprofile_name_help', max_length=100)),
 | 
				
			||||||
 | 
					                ('video_map_stream', models.PositiveIntegerField(default=0, help_text='transcodingprofile_video_map_stream_help')),
 | 
				
			||||||
 | 
					                ('video_codec', models.CharField(choices=[('copy', 'Copy'), ('h264', 'H.264'), ('h265', 'H.265')], help_text='transcodingprofile_video_codec_help', max_length=4)),
 | 
				
			||||||
 | 
					                ('video_bitrate', models.PositiveIntegerField(default=6000, help_text='transcodingprofile_video_bitrate_help')),
 | 
				
			||||||
 | 
					                ('video_frame_rate', models.PositiveIntegerField(default=30, help_text='transcodingprofile_video_frame_rate_help')),
 | 
				
			||||||
 | 
					                ('video_resolution', models.CharField(help_text='transcodingprofile_video_resolution_help', max_length=9)),
 | 
				
			||||||
 | 
					                ('video_gop_size', models.PositiveIntegerField(default=60, help_text='transcodingprofile_video_gop_size_help')),
 | 
				
			||||||
 | 
					                ('video_pixel_format', models.CharField(choices=[('yuv420', 'YUV420'), ('yuv422', 'YUV422'), ('yuv444', 'YUV444')], help_text='transcodingprofile_video_pixel_format_help', max_length=10)),
 | 
				
			||||||
 | 
					                ('video_bitrate_mode', models.CharField(choices=[('cbr', 'CBR'), ('vbr', 'VBR')], help_text='transcodingprofile_video_bitrate_mode_help', max_length=10)),
 | 
				
			||||||
 | 
					                ('audio_map_stream', models.PositiveIntegerField(default=0, help_text='transcodingprofile_audio_map_stream_help')),
 | 
				
			||||||
 | 
					                ('audio_codec', models.CharField(choices=[('copy', 'Copy'), ('aac', 'AAC')], help_text='transcodingprofile_audio_codec_help', max_length=10)),
 | 
				
			||||||
 | 
					                ('audio_bitrate', models.PositiveIntegerField(default=160, help_text='transcodingprofile_audio_bitrate_help')),
 | 
				
			||||||
 | 
					                ('audio_channels', models.PositiveIntegerField(default=2, help_text='transcodingprofile_audio_channels_help')),
 | 
				
			||||||
 | 
					                ('audio_sample_rate', models.PositiveIntegerField(default=48000, help_text='transcodingprofile_audio_sample_rate_help')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'transcodingprofile_verbose_name',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'transcodingprofile_verbose_name_plural',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        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),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='pull',
 | 
				
			||||||
 | 
					            name='transcodingprofile',
 | 
				
			||||||
 | 
					            field=models.ForeignKey(blank=True, help_text='restream_transcodingprofile_help', null=True, on_delete=django.db.models.deletion.PROTECT, to='config.transcodingprofile'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='restream',
 | 
				
			||||||
 | 
					            name='transcodingprofile',
 | 
				
			||||||
 | 
					            field=models.ForeignKey(blank=True, help_text='restream_transcodingprofile_help', null=True, on_delete=django.db.models.deletion.PROTECT, to='config.transcodingprofile'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					# Generated by Django 5.0.2 on 2024-04-13 08:52
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import django.db.models.deletion
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('config', '0005_transcodingprofile_alter_stream_name_and_more'),
 | 
				
			||||||
 | 
					        ('contenttypes', '0002_remove_content_type_name'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='LocalRecordingStorage',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(help_text='recordingstorage_name_help', max_length=100)),
 | 
				
			||||||
 | 
					                ('path', models.CharField(help_text='localrecordingstorage_path_help', max_length=500)),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'localrecordingstorage_verbose_name',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'localrecordingstorage_verbose_name_plural',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='S3RecordingStorage',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(help_text='recordingstorage_name_help', max_length=100)),
 | 
				
			||||||
 | 
					                ('bucket', models.CharField(help_text='s3recordingstorage_bucket_help', max_length=100)),
 | 
				
			||||||
 | 
					                ('access_key', models.CharField(help_text='s3recordingstorage_access_key_help', max_length=100)),
 | 
				
			||||||
 | 
					                ('secret_key', models.CharField(help_text='s3recordingstorage_secret_key_help', max_length=100)),
 | 
				
			||||||
 | 
					                ('region', models.CharField(help_text='s3recordingstorage_region_help', max_length=100)),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 's3recordingstorage_verbose_name',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 's3recordingstorage_verbose_name_plural',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='Recorder',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(help_text='recorder_name_help', max_length=100)),
 | 
				
			||||||
 | 
					                ('active', models.BooleanField(help_text='recorder_activate_help')),
 | 
				
			||||||
 | 
					                ('storage_config_id', models.PositiveIntegerField()),
 | 
				
			||||||
 | 
					                ('storage_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(('app_label', 'config'), ('model', 'LocalRecordingStorage')), models.Q(('app_label', 'app'), ('model', 'S3RecordingStorage')), _connector='OR'), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
 | 
				
			||||||
 | 
					                ('stream', models.ForeignKey(help_text='recorder_stream_help', on_delete=django.db.models.deletion.CASCADE, to='config.stream')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,9 @@
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib.contenttypes.fields import GenericForeignKey
 | 
				
			||||||
 | 
					from django.contrib.contenttypes.models import ContentType
 | 
				
			||||||
from django.conf import settings
 | 
					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
 | 
				
			||||||
| 
						 | 
					@ -10,6 +13,70 @@ from portier.common import handlers
 | 
				
			||||||
from config import signals_shared
 | 
					from config import signals_shared
 | 
				
			||||||
from config.util import validate_stream_url
 | 
					from config.util import validate_stream_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TranscodingProfile(models.Model):
 | 
				
			||||||
 | 
					    VIDEO_CODECS = (
 | 
				
			||||||
 | 
					        ('copy', 'Copy'),
 | 
				
			||||||
 | 
					        ('h264', 'H.264'),
 | 
				
			||||||
 | 
					        ('h265', 'H.265'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    AUDIO_CODECS = (
 | 
				
			||||||
 | 
					        ('copy', 'Copy'),
 | 
				
			||||||
 | 
					        ('aac', 'AAC'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    VIDEO_PIXEL_FORMATS = (
 | 
				
			||||||
 | 
					        ('yuv420', 'YUV420'),
 | 
				
			||||||
 | 
					        ('yuv422', 'YUV422'),
 | 
				
			||||||
 | 
					        ('yuv444', 'YUV444'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    VIDEO_BITRATE_MODES = (
 | 
				
			||||||
 | 
					        ('cbr', 'CBR'),
 | 
				
			||||||
 | 
					        ('vbr', 'VBR'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    name = models.CharField(max_length=100, help_text=_('transcodingprofile_name_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    video_map_stream = models.PositiveIntegerField(default=0, help_text=_('transcodingprofile_video_map_stream_help'))
 | 
				
			||||||
 | 
					    video_codec = models.CharField(max_length=4, choices=VIDEO_CODECS, help_text=_('transcodingprofile_video_codec_help'))
 | 
				
			||||||
 | 
					    video_bitrate = models.PositiveIntegerField(default=6000, help_text=_('transcodingprofile_video_bitrate_help'))
 | 
				
			||||||
 | 
					    video_frame_rate = models.PositiveIntegerField(default=30, help_text=_('transcodingprofile_video_frame_rate_help'))
 | 
				
			||||||
 | 
					    video_resolution = models.CharField(max_length=9, help_text=_('transcodingprofile_video_resolution_help'))
 | 
				
			||||||
 | 
					    video_gop_size = models.PositiveIntegerField(default=60, help_text=_('transcodingprofile_video_gop_size_help'))
 | 
				
			||||||
 | 
					    video_pixel_format = models.CharField(max_length=10, choices=VIDEO_PIXEL_FORMATS, help_text=_('transcodingprofile_video_pixel_format_help'))
 | 
				
			||||||
 | 
					    video_bitrate_mode = models.CharField(max_length=10, choices=VIDEO_BITRATE_MODES, help_text=_('transcodingprofile_video_bitrate_mode_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    audio_map_stream = models.PositiveIntegerField(default=0, help_text=_('transcodingprofile_audio_map_stream_help'))
 | 
				
			||||||
 | 
					    audio_codec = models.CharField(max_length=10, choices=AUDIO_CODECS, help_text=_('transcodingprofile_audio_codec_help'))
 | 
				
			||||||
 | 
					    audio_bitrate = models.PositiveIntegerField(default=160, help_text=_('transcodingprofile_audio_bitrate_help'))
 | 
				
			||||||
 | 
					    audio_channels = models.PositiveIntegerField(default=2, help_text=_('transcodingprofile_audio_channels_help'))
 | 
				
			||||||
 | 
					    audio_sample_rate = models.PositiveIntegerField(default=48000, help_text=_('transcodingprofile_audio_sample_rate_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _('transcodingprofile_verbose_name')
 | 
				
			||||||
 | 
					        verbose_name_plural = _('transcodingprofile_verbose_name_plural')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def class_name(self):
 | 
				
			||||||
 | 
					        return _('transcodingprofile_class_name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
 | 
					        return reverse('config:transcodingprofile_detail', kwargs={'pk': self.pk})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return str(self.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_concierge_configuration(self):
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            'config_version': 1,
 | 
				
			||||||
 | 
					            'video_map_stream': self.video_map_stream,
 | 
				
			||||||
 | 
					            'video_codec': self.video_codec,
 | 
				
			||||||
 | 
					            'video_bitrate': self.video_bitrate,
 | 
				
			||||||
 | 
					            'video_frame_rate': self.video_frame_rate,
 | 
				
			||||||
 | 
					            'video_resolution': self.video_resolution,
 | 
				
			||||||
 | 
					            'audio_map_stream': self.audio_map_stream,
 | 
				
			||||||
 | 
					            'audio_codec': self.audio_codec,
 | 
				
			||||||
 | 
					            'audio_bitrate': self.audio_bitrate,
 | 
				
			||||||
 | 
					            'audio_channels': self.audio_channels,
 | 
				
			||||||
 | 
					            'audio_sample_rate': self.audio_sample_rate,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Stream(models.Model):
 | 
					class Stream(models.Model):
 | 
				
			||||||
    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'))
 | 
				
			||||||
| 
						 | 
					@ -45,9 +112,6 @@ class Stream(models.Model):
 | 
				
			||||||
        return str(self.name)
 | 
					        return str(self.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Stream)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SRSNode(models.Model):
 | 
					class SRSNode(models.Model):
 | 
				
			||||||
    name = models.CharField(max_length=100, help_text=_('srsnode_name_help'))
 | 
					    name = models.CharField(max_length=100, help_text=_('srsnode_name_help'))
 | 
				
			||||||
    api_base = models.CharField(max_length=256, help_text=_('srsnode_api_base_help'))
 | 
					    api_base = models.CharField(max_length=256, help_text=_('srsnode_api_base_help'))
 | 
				
			||||||
| 
						 | 
					@ -81,6 +145,7 @@ class Pull(models.Model):
 | 
				
			||||||
    source = models.CharField(max_length=500, validators=[validate_stream_url], help_text=_('pull_source_help'))
 | 
					    source = models.CharField(max_length=500, validators=[validate_stream_url], help_text=_('pull_source_help'))
 | 
				
			||||||
    active = models.BooleanField(help_text=_('pull_activate_help'))
 | 
					    active = models.BooleanField(help_text=_('pull_activate_help'))
 | 
				
			||||||
    name = models.CharField(max_length=100, help_text=_('pull_name_help'))
 | 
					    name = models.CharField(max_length=100, help_text=_('pull_name_help'))
 | 
				
			||||||
 | 
					    transcodingprofile = models.ForeignKey(TranscodingProfile, null=True, blank=True, on_delete=models.PROTECT, help_text=_('restream_transcodingprofile_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        verbose_name = _('pull_verbose_name')
 | 
					        verbose_name = _('pull_verbose_name')
 | 
				
			||||||
| 
						 | 
					@ -116,17 +181,16 @@ class Pull(models.Model):
 | 
				
			||||||
        node = SRSNode.objects.filter(active=True).order_by('?').first()
 | 
					        node = SRSNode.objects.filter(active=True).order_by('?').first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
 | 
					            'config_version': 1,
 | 
				
			||||||
            "type": "pull",
 | 
					            "type": "pull",
 | 
				
			||||||
            "active": self.active,
 | 
					            "active": self.active,
 | 
				
			||||||
            "name": self.name,
 | 
					            "name": self.name,
 | 
				
			||||||
            "source": self.source,
 | 
					            "source": self.source,
 | 
				
			||||||
            "target": f"{node.rtmp_base}/{settings.GLOBAL_STREAM_NAMESPACE}/{self.stream.stream}",
 | 
					            "target": f"{node.rtmp_base}/{settings.GLOBAL_STREAM_NAMESPACE}/{self.stream.stream}",
 | 
				
			||||||
 | 
					            'transcoding_profile': self.transcodingprofile.get_concierge_configuration(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Pull)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Restream(models.Model):
 | 
					class Restream(models.Model):
 | 
				
			||||||
    FORMATS = (
 | 
					    FORMATS = (
 | 
				
			||||||
        ('flv', 'flv (RTMP)'),
 | 
					        ('flv', 'flv (RTMP)'),
 | 
				
			||||||
| 
						 | 
					@ -137,6 +201,7 @@ class Restream(models.Model):
 | 
				
			||||||
    name = models.CharField(max_length=100, help_text=_('restream_name_help'))
 | 
					    name = models.CharField(max_length=100, help_text=_('restream_name_help'))
 | 
				
			||||||
    active = models.BooleanField(help_text=_('restream_activate_help'))
 | 
					    active = models.BooleanField(help_text=_('restream_activate_help'))
 | 
				
			||||||
    format = models.CharField(max_length=6, choices=FORMATS, default='flv', help_text=_('restream_format_help'))
 | 
					    format = models.CharField(max_length=6, choices=FORMATS, default='flv', help_text=_('restream_format_help'))
 | 
				
			||||||
 | 
					    transcodingprofile = models.ForeignKey(TranscodingProfile, null=True, blank=True, on_delete=models.PROTECT, help_text=_('restream_transcodingprofile_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        verbose_name = _('restream_verbose_name')
 | 
					        verbose_name = _('restream_verbose_name')
 | 
				
			||||||
| 
						 | 
					@ -176,7 +241,53 @@ class Restream(models.Model):
 | 
				
			||||||
            'stream_source_url': f"{rtmp_base}/{settings.GLOBAL_STREAM_NAMESPACE}/{self.stream.stream}",
 | 
					            'stream_source_url': f"{rtmp_base}/{settings.GLOBAL_STREAM_NAMESPACE}/{self.stream.stream}",
 | 
				
			||||||
            'stream_target_url': self.target,
 | 
					            'stream_target_url': self.target,
 | 
				
			||||||
            'stream_target_transport': self.format,
 | 
					            'stream_target_transport': self.format,
 | 
				
			||||||
 | 
					            'transcoding_profile': self.transcodingprofile.get_concierge_configuration(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Stream)
 | 
				
			||||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Restream)
 | 
					pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Restream)
 | 
				
			||||||
 | 
					pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Pull)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Recording Storage configurations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RecordingStorage(models.Model):
 | 
				
			||||||
 | 
					    name = models.CharField(max_length=100, help_text=_('recordingstorage_name_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LocalRecordingStorage(RecordingStorage):
 | 
				
			||||||
 | 
					    path = models.CharField(max_length=500, help_text=_('localrecordingstorage_path_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _('localrecordingstorage_verbose_name')
 | 
				
			||||||
 | 
					        verbose_name_plural = _('localrecordingstorage_verbose_name_plural')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class S3RecordingStorage(RecordingStorage):
 | 
				
			||||||
 | 
					    bucket = models.CharField(max_length=100, help_text=_('s3recordingstorage_bucket_help'))
 | 
				
			||||||
 | 
					    access_key = models.CharField(max_length=100, help_text=_('s3recordingstorage_access_key_help'))
 | 
				
			||||||
 | 
					    secret_key = models.CharField(max_length=100, help_text=_('s3recordingstorage_secret_key_help'))
 | 
				
			||||||
 | 
					    region = models.CharField(max_length=100, help_text=_('s3recordingstorage_region_help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _('s3recordingstorage_verbose_name')
 | 
				
			||||||
 | 
					        verbose_name_plural = _('s3recordingstorage_verbose_name_plural')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Recorder(models.Model):
 | 
				
			||||||
 | 
					    storage_type_models = models.Q(app_label = 'config', model = 'LocalRecordingStorage') | models.Q(app_label = 'app', model = 'S3RecordingStorage')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stream = models.ForeignKey(Stream, on_delete=models.CASCADE, help_text=_('recorder_stream_help'))
 | 
				
			||||||
 | 
					    name = models.CharField(max_length=100, help_text=_('recorder_name_help'))
 | 
				
			||||||
 | 
					    active = models.BooleanField(help_text=_('recorder_activate_help'))
 | 
				
			||||||
 | 
					    storage_type = models.ForeignKey(ContentType, limit_choices_to=storage_type_models, on_delete=models.CASCADE)
 | 
				
			||||||
 | 
					    storage_config_id = models.PositiveIntegerField()
 | 
				
			||||||
 | 
					    storage_config = GenericForeignKey('storage_type', 'storage_config_id')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,8 @@ from django.conf import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PERMISSIONS = [
 | 
					PERMISSIONS = [
 | 
				
			||||||
    'add_stream',
 | 
					    'add_stream',
 | 
				
			||||||
    'add_restream'
 | 
					    'add_restream',
 | 
				
			||||||
 | 
					    'add_pull',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,10 @@ from ninja import NinjaAPI, ModelSchema, Router
 | 
				
			||||||
from ninja.security import django_auth
 | 
					from ninja.security import django_auth
 | 
				
			||||||
from django.contrib.auth.models import User
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from config.api import router as config_router
 | 
					import config.api as config_api
 | 
				
			||||||
 | 
					#from config.api import router as config_router
 | 
				
			||||||
 | 
					#from config.api.recorder import router as config_recorder_router
 | 
				
			||||||
 | 
					#from config.api.recorder import router as config_recorder_router
 | 
				
			||||||
from concierge.api import router as concierge_router
 | 
					from concierge.api import router as concierge_router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
core_router = Router()
 | 
					core_router = Router()
 | 
				
			||||||
| 
						 | 
					@ -25,6 +28,9 @@ api = NinjaAPI(
 | 
				
			||||||
  csrf=False, # Disable CSRF for now
 | 
					  csrf=False, # Disable CSRF for now
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
api.add_router("/", core_router, auth=django_auth, tags=["Core"])
 | 
					api.add_router("/", core_router, auth=django_auth, tags=["Core API"])
 | 
				
			||||||
api.add_router("/config/", config_router, auth=django_auth, tags=["Configuration"])
 | 
					api.add_router("/config/recorder/", config_api.recorder.router, auth=django_auth, tags=["Recorder Configuration API"])
 | 
				
			||||||
api.add_router("/concierge/", concierge_router, auth=None, tags=["Concierge"])
 | 
					api.add_router("/config/pull/", config_api.pull.router, auth=django_auth, tags=["Pull Configuration API"])
 | 
				
			||||||
 | 
					api.add_router("/config/stream/", config_api.stream.router, auth=django_auth, tags=["Stream Configuration API"])
 | 
				
			||||||
 | 
					api.add_router("/config/restream/", config_api.restream.router, auth=django_auth, tags=["Resteam Configuration API"])
 | 
				
			||||||
 | 
					api.add_router("/concierge/", concierge_router, auth=None, tags=["Concierge API"])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue