backend/src/resources.py

515 lines
19 KiB
Python

#!/usr/bin/env python3
from uuid import UUID
import datetime
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from flask_restful import Resource
from flask import request, current_app, abort
from db import db
from models import VideonUser, StreamResource, StreamResourceTypeEnum, OutputUrls
from schemas import UserSchema, UserMetadataSchema, StreamResourceSchema, IngestInputSchema, EncodeInputSchema, \
RestreamInputSchema
from config import REGISTER_DISABLED
import listdiffer
"""
Flask Restful endpoints
"""
__author__ = '@tormakris'
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "resources"
__version__text__ = "1"
INVALID_JSON_SCHEMA_MSG = "invalid json schema"
DB_ERROR_MSG = "db session error"
class SignupApi(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createuser
"""
userschema = UserSchema(many=False)
usermetadataschema = UserMetadataSchema(many=False)
def post(self):
if REGISTER_DISABLED:
abort(401, "register disabled")
body = request.get_json()
try:
userobj = self.userschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
user = VideonUser(name=userobj['name'], password=userobj['password'])
try:
user.hash_password()
db.session.add(user)
except Exception as e:
current_app.logger.warning(e)
abort(503, "user already exists")
db.session.commit()
return self.usermetadataschema.dump(user), 200
class LoginApi(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/logon
"""
userschema = UserSchema(many=False)
usermetadataschema = UserMetadataSchema(many=False)
def post(self):
body = request.get_json()
try:
userobj = self.userschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
user = VideonUser.query.filter_by(name=userobj['name']).first_or_404()
authorized = user.check_password(userobj['password'])
if not authorized:
abort(401, "username or password incorrect")
try:
user.last_logon = datetime.datetime.now()
db.session.add(user)
except Exception as e:
current_app.logger.exception(e)
abort(503, DB_ERROR_MSG)
expires = datetime.timedelta(days=7)
access_token = create_access_token(identity=str(user.name), expires_delta=expires)
db.session.commit()
return {'token': access_token}, 200
class UsersApi(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getall
"""
usermetadataschema = UserMetadataSchema(many=True)
def get(self):
users = VideonUser.query.all()
return self.usermetadataschema.dump(users), 200
class UserParameterApi(Resource):
userschema = UserSchema(many=False)
usermetadataschema = UserMetadataSchema(many=False)
def get(self, username: str):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getauser
:param username: Username of user (url parameter)
:return:
"""
user = VideonUser.query.filter_by(name=username).first_or_404()
return self.usermetadataschema.dump(user), 200
def delete(self, username: str):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/deleteuser
:param username: Username of user (url parameter)
:return:
"""
user = VideonUser.query.filter_by(name=username).first_or_404()
try:
db.session.delete(user)
except Exception as e:
current_app.logger.exception(e)
abort(503, DB_ERROR_MSG)
db.session.commit()
return self.usermetadataschema.dump(user), 200
def put(self, username: str):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/modifyUser
:param username: Username of user (url parameter)
:return:
"""
body = request.get_json()
user = VideonUser.query.filter_by(name=username).first_or_404()
try:
userobj = self.userschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
try:
user.password = userobj['password']
user.hash_password()
except Exception as e:
current_app.logger.exception(e)
abort(503, DB_ERROR_MSG)
db.session.commit()
return self.usermetadataschema.dump(user), 200
class CreateIngestResource(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createIngest
"""
ingestinputschema = IngestInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def post(self):
body = request.get_json()
try:
ingestobj = self.ingestinputschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
ingest = StreamResource(resource_type=StreamResourceTypeEnum['INGEST'],
url="rtmp://zelenka.tormakristof.eu:6969/origin/asdasd", x=ingestobj['x'],y=ingestobj['y'])
# username = get_jwt_identity()
username = "jozska"
user = VideonUser.query.filter_by(name=username).first_or_404()
ingest.owner_id = user.id
try:
db.session.add(ingest)
except Exception as e:
current_app.logger.warning(e)
abort(503, "object already exists")
if 'outputNeighbours' in ingestobj:
for neighbour in ingestobj['outputNeighbours']:
neighbourobj = StreamResource.query.filter_by(id=neighbour).first_or_404()
if neighbourobj.resource_type == StreamResourceTypeEnum.INGEST:
abort(400, "ingest cannot be a downstream component")
try:
neighbourobj.parent_id = ingest.id
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not set neighbourhood")
db.session.commit()
return self.streamresourceschema.dump(ingest), 200
class CreateRestreamResource(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createRestream
"""
restreaminputschema = RestreamInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def post(self):
body = request.get_json()
try:
restreamobj = self.restreaminputschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
restream = StreamResource(resource_type=StreamResourceTypeEnum['RESTREAM'],
url="rtmp://zelenka.tormakristof.eu:6969/origin/asdasd",x=restreamobj['x'],y=restreamobj['y'])
# username = get_jwt_identity()
username = "jozska"
user = VideonUser.query.filter_by(name=username).first_or_404()
restream.owner_id = user.id
if 'inputNeighbour' in restreamobj:
restream.parent_id = restreamobj['inputNeighbour']
try:
db.session.add(restream)
db.session.commit()
except Exception as e:
current_app.logger.warning(e)
abort(503, "object already exists")
for url in restreamobj['outputURLs']:
try:
outputurl = OutputUrls(output_url=url, streamresource_id=restream.id)
db.session.add(outputurl)
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not add all outputurls")
db.session.commit()
return self.streamresourceschema.dump(restream), 200
class CreateEncodeResource(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createEncode
"""
encodeinputschema = EncodeInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def post(self):
body = request.get_json()
try:
encoderobj = self.encodeinputschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
encoder = StreamResource(resource_type=StreamResourceTypeEnum['ENCODE'],
url="rtmp://zelenka.tormakristof.eu:6969/origin/asdasd",x=encoderobj['x'],y=encoderobj['y'])
# username = get_jwt_identity()
username = "jozska"
user = VideonUser.query.filter_by(name=username).first_or_404()
encoder.owner_id = user.id
if 'inputNeighbour' in encoderobj:
encoder.parent_id = encoderobj['inputNeighbour']
if 'bitrate' in encoderobj:
encoder.bitrate = encoderobj['bitrate']
if 'width' in encoderobj:
encoder.bitrate = encoderobj['width']
if 'height' in encoderobj:
encoder.bitrate = encoderobj['height']
try:
db.session.add(encoder)
except Exception as e:
current_app.logger.warning(e)
abort(503, "object already exists")
for neighbour in encoderobj['outputNeighbours']:
neighbour = StreamResource.query.filter_by(id=neighbour).first_or_404()
if neighbour.resource_type == StreamResourceTypeEnum.INGEST:
abort(400, "ingest cannot be a downstream component")
try:
neighbour.parent_id = encoder.id
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not add all neighbours")
db.session.commit()
return self.streamresourceschema.dump(encoder), 200
class GetAllStreamResources(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getResource
"""
streamresourceschema = StreamResourceSchema(many=True)
def get(self):
streamreousrces = StreamResource.query.all()
return self.streamresourceschema.dump(streamreousrces), 200
class ManipulateStreamResource(Resource):
streamresourceschema = StreamResourceSchema(many=False)
def get(self, resourceid: str):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getAResource
:param resourceid:
:return:
"""
streamreousrce = StreamResource.query.filter_by(id=UUID(resourceid)).first_or_404()
return self.streamresourceschema.dump(streamreousrce), 200
def delete(self, resourceid: str):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/deleteResource
:param resourceid:
:return:
"""
streamreousrce = StreamResource.query.filter_by(id=UUID(resourceid)).first_or_404()
try:
db.session.delete(streamreousrce)
except Exception as e:
current_app.logger.exception(e)
abort(503, DB_ERROR_MSG)
db.session.commit()
return self.streamresourceschema.dump(streamreousrce), 200
class ModifyIngressResource(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editIngress
"""
ingestinputschema = IngestInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def put(self, resourceid: str):
body = request.get_json()
try:
ingestobj = self.ingestinputschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
ingest = StreamResource.query.filter_by(id=resourceid).first_or_404()
ingest.x = ingestobj['x']
ingest.y = ingestobj['y']
currentoutputneighbours = ingest.children
currentoutputneighboursids = [outputneighbour.id for outputneighbour in currentoutputneighbours]
if 'outputNeighbours' in ingestobj:
for removeneighbours in listdiffer.elementsinfirstlistbutnotinsecond(currentoutputneighboursids,
ingestobj['outputNeighbours']):
neighbourobj = StreamResource.query.filter_by(id=removeneighbours).first_or_404()
try:
neighbourobj.parent_id = None
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not remove neighbourhood")
for addneighbours in listdiffer.elementsinsecondlistbutnotinfirst(currentoutputneighboursids,
ingestobj['outputNeighbours']):
neighbourobj = StreamResource.query.filter_by(id=addneighbours).first_or_404()
if neighbourobj.resource_type == StreamResourceTypeEnum.INGEST:
abort(400, "ingest cannot be a downstream component")
try:
neighbourobj.parent_id = ingest.id
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not remove neighbourhood")
else:
if currentoutputneighbours:
for currentneighbour in currentoutputneighbours:
try:
currentneighbour.parent_id = None
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not remove neighbourhood")
db.session.commit()
return self.streamresourceschema.dump(ingest), 200
class ModifyRestreamResource(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editRestream
"""
restreaminputschema = RestreamInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def put(self, resourceid: str):
body = request.get_json()
try:
restreamobj = self.restreaminputschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
restream = StreamResource.query.filter_by(id=resourceid).first_or_404()
restream.x = restreamobj['x']
restream.y = restreamobj['y']
if 'inputNeighbour' in restreamobj:
try:
restream.parent_id = restreamobj['inputNeighbour']
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not set parent id")
currentoutputurls = OutputUrls.query.filter_by(streamresource_id=restream.id).all()
currentoutputurlurls = [currentoutputurl.output_url for currentoutputurl in currentoutputurls]
for deleteoutputurl in listdiffer.elementsinfirstlistbutnotinsecond(currentoutputurlurls,
restreamobj['outputURLs']):
elementtodelete = OutputUrls.query.filter_by(output_url=deleteoutputurl,
streamresource_id=restream.id).first_or_404()
try:
db.session.delete(elementtodelete)
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not delete url")
for addoutputurl in listdiffer.elementsinsecondlistbutnotinfirst(currentoutputurlurls,
restreamobj['outputURLs']):
elementtoadd = OutputUrls(output_url=addoutputurl, streamresource_id=restream.id)
try:
db.session.add(elementtoadd)
except Exception as e:
current_app.logger.warning(e)
abort(503, "object already exists")
db.session.commit()
return self.streamresourceschema.dump(restream), 200
class ModifyEncodeResource(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editEncode
"""
encodeinputschema = EncodeInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def put(self, resourceid: str):
body = request.get_json()
try:
encodeobj = self.encodeinputschema.load(body)
except Exception as e:
current_app.logger.warning(e)
abort(417, INVALID_JSON_SCHEMA_MSG)
encode = StreamResource.query.filter_by(id=resourceid).first_or_404()
encode.x = encodeobj['x']
encode.y = encodeobj['y']
if 'inputNeighbour' in encodeobj:
encode.parent_id = encodeobj['inputNeighbour']
if 'bitrate' in encodeobj:
encode.bitrate = encodeobj['bitrate']
if 'width' in encodeobj:
encode.bitrate = encodeobj['width']
if 'height' in encodeobj:
encode.bitrate = encodeobj['height']
currentoutputneighbours = encode.children
currentoutputneighboursids = []
for outputneighbour in currentoutputneighbours:
currentoutputneighboursids.append(outputneighbour.id)
if 'outputNeighbours' in encodeobj:
for removeneighbours in listdiffer.elementsinfirstlistbutnotinsecond(currentoutputneighbours,
encodeobj['outputNeighbours']):
neighbourobj = StreamResource.query.filter_by(id=removeneighbours).first_or_404()
neighbourobj.parent_id = None
for addneighbours in listdiffer.elementsinsecondlistbutnotinfirst(currentoutputneighbours,
encodeobj['outputNeighbours']):
neighbourobj = StreamResource.query.filter_by(id=addneighbours).first_or_404()
if neighbourobj.resource_type == StreamResourceTypeEnum.INGEST:
abort(400, "ingest cannot be a downstream component")
neighbourobj.parent_id = encode.id
else:
if currentoutputneighbours:
for currentneighbour in currentoutputneighbours:
try:
currentneighbour.parent_id = None
except Exception as e:
current_app.logger.warning(e)
abort(503, "could not remove neighbourhood")
db.session.commit()
return self.streamresourceschema.dump(encode), 200