much work done
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Torma Kristóf 2020-11-25 05:16:36 +01:00
parent 603d08cc61
commit 2c89f3d0c3
7 changed files with 493 additions and 55 deletions

View File

@ -7,6 +7,7 @@ psycopg2-binary
sqlalchemy sqlalchemy
flask_sqlalchemy flask_sqlalchemy
marshmallow marshmallow
marshmallow_enum
marshmallow-sqlalchemy marshmallow-sqlalchemy
flask-marshmallow flask-marshmallow
py-healthcheck py-healthcheck

View File

@ -8,7 +8,8 @@ from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from healthcheck import HealthCheck from healthcheck import HealthCheck
from errorhandlers import register_all_error_handlers from errorhandlers import register_all_error_handlers
from config import * from config import SENTRY_DSN, RELEASE_ID, RELEASEMODE, POSTGRES_DB, PORT, POSTGRES_HOSTNAME, POSTGRES_PASSWORD, \
POSTGRES_USERNAME, DEBUG, JWT_SECRET_KEY
from db import db from db import db
from jwtman import jwtman from jwtman import jwtman
from fbcrypt import bcrypt from fbcrypt import bcrypt

99
src/kuberclient.py Normal file
View File

@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""
Kubernetes Client Tools
"""
__author__ = "@tormakris"
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "kuberclient"
__version__text__ = "1"
from kubernetes import client, config
class Kubectl:
def __init__(self, name: str, resourcetype: str = "ingest", stream_key: str = "test", push_urls: str = "",
ffmpeg_args: str = "", encode_push_urls: str = ""):
config.load_incluster_config()
self.name = name
self.streamtype = resourcetype.lower(),
self.stream_key = stream_key,
self.push_urls = push_urls,
self.ffmpeg_args = ffmpeg_args,
self.encode_push_url = encode_push_urls
def __delete_pod(self):
foundpods = client.CoreV1Api().list_namespaced_pod("videon", label_selector=f"app in ({self.name})")
for pod in foundpods.items:
client.CoreV1Api().delete_namespaced_pod(pod.metadata.name, "videon")
def __create_configmap(self):
configmap = client.V1ConfigMap(api_version="v1", kind="ConfigMap",
data={"TYPE": self.streamtype, "STREAM_KEY": self.stream_key,
"PUSH_URLS": self.push_urls, "FFMPEG_ARGS": self.ffmpeg_args,
"ENCODE_PUSH_URL": self.encode_push_url})
client.CoreV1Api().create_namespaced_config_map(namespace="videon", name=self.name, body=configmap)
def __update_configmap(self):
configmap = client.V1ConfigMap(api_version="v1", kind="ConfigMap",
data={"TYPE": self.streamtype, "STREAM_KEY": self.stream_key,
"PUSH_URLS": self.push_urls, "FFMPEG_ARGS": self.ffmpeg_args,
"ENCODE_PUSH_URL": self.encode_push_url})
client.CoreV1Api().patch_namespaced_config_map(namespace="videon",name=self.name, body=configmap)
def __create_deployment(self):
envs = [client.V1EnvFromSource(config_map_ref=client.V1ConfigMapEnvSource(name=self.name))]
container = client.V1Container(
name=self.name,
image="registry.kmlabz.com/videon/nginx-streamer",
image_pull_policy="Always",
ports=[client.V1ContainerPort(container_port=1935)],
env_from=envs,
)
# Template
template = client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels={"app": self.name}),
spec=client.V1PodSpec(containers=[container]))
# Spec
spec = client.V1DeploymentSpec(
replicas=1,
template=template)
# Deployment
deployment = client.V1Deployment(
api_version="apps/v1",
kind="Deployment",
metadata=client.V1ObjectMeta(name=self.name),
spec=spec)
# Creation of the Deployment in specified namespace
client.AppsV1Api().create_namespaced_deployment(
namespace="videon", body=deployment
)
def __create_service(self):
core_v1_api = client.CoreV1Api()
body = client.V1Service(
api_version="v1",
kind="Service",
metadata=client.V1ObjectMeta(
name=self.name
),
spec=client.V1ServiceSpec(
selector={"app": self.name},
ports=[client.V1ServicePort(
port=1935,
target_port=1935
)]
)
)
# Creation of the Service in specified namespace
core_v1_api.create_namespaced_service(namespace="videon", body=body)
def create_resource(self):
self.__create_configmap()
self.__create_deployment()
self.__create_service()
def update_resource(self):
self.__update_configmap()
self.__delete_pod()

18
src/listdiffer.py Normal file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
"""
Python list differs
"""
__author__ = '@tormakris'
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "listdiffer"
__version__text__ = "1"
def elementsinfirstlistbutnotinsecond(li1, li2):
return list(set(li1) - set(li2))
def elementsinsecondlistbutnotinfirst(li1, li2):
return list(set(li2)-set(li1))

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from uuid import uuid4
from enum import Enum from enum import Enum
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import backref
from sqlalchemy.sql import func from sqlalchemy.sql import func
from flask_bcrypt import generate_password_hash, check_password_hash from flask_bcrypt import generate_password_hash, check_password_hash
@ -18,7 +18,7 @@ __module_name__ = "models"
__version__text__ = "1" __version__text__ = "1"
class User(db.Model): class VideonUser(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String, nullable=False, unique=True) name = db.Column(db.String, nullable=False, unique=True)
@ -42,18 +42,23 @@ class StreamResourceTypeEnum(Enum):
class StreamResource(db.Model): class StreamResource(db.Model):
id = db.Column(UUID(as_uuid=True), primary_key=True, unique=True, nullable=False, default=uuid4) id = db.Column(UUID(as_uuid=True), primary_key=True, unique=True, nullable=False,
server_default=db.text("uuid_generate_v4()"))
resource_type = db.Column(db.Enum(StreamResourceTypeEnum), nullable=False) resource_type = db.Column(db.Enum(StreamResourceTypeEnum), nullable=False)
url = db.Column(db.String, nullable=True) url = db.Column(db.String, nullable=False)
output_urls = db.relationship('OuputUrls', backref='streamresource', lazy=False)
bitrate = db.Column(db.Integer, nullable=True) bitrate = db.Column(db.Integer, nullable=True)
width = db.Column(db.Integer, nullable=True) width = db.Column(db.Integer, nullable=True)
height = db.Column(db.Integer, nullable=True) height = db.Column(db.Integer, nullable=True)
parent_id = db.Column(db.Integer, db.ForeignKey('stream_resource.id'), nullable=True) parent_id = db.Column(UUID(as_uuid=True), db.ForeignKey('stream_resource.id'), nullable=True)
children = db.relationship('StreamResource', lazy=False) children = db.relationship('StreamResource', lazy=False)
owner_id = db.Column(db.Integer, db.ForeignKey('videon_user.id'), nullable=False)
owner = db.relationship('VideonUser', backref=backref("stream_resource", cascade="all, delete"), lazy=False)
x = db.Column(db.Integer, nullable=False)
y = db.Column(db.Integer, nullable=False)
class OuputUrls(db.Model): class OutputUrls(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
output_url = db.Column(db.String, nullable=False) output_url = db.Column(db.String, nullable=False)
streamresource_id = db.Column(db.Integer, db.ForeignKey('stream_resource.id'), nullable=False) streamresource_id = db.Column(UUID(as_uuid=True), db.ForeignKey('stream_resource.id'), nullable=False)
streamresource = db.relationship('StreamResource', backref=backref('output_urls', cascade="all, delete"), lazy=False)

View File

@ -1,15 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from uuid import uuid4 from uuid import UUID
import datetime import datetime
from flask_jwt_extended import create_access_token, jwt_required from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from flask_restful import Resource from flask_restful import Resource
from flask import request, current_app, abort from flask import request, current_app, abort
from db import db from db import db
from models import User from models import VideonUser, StreamResource, StreamResourceTypeEnum, OutputUrls
from schemas import UserSchema, UserMetadataSchema from schemas import UserSchema, UserMetadataSchema, StreamResourceSchema, IngestInputSchema, EncodeInputSchema, \
RestreamInputSchema
from config import REGISTER_DISABLED from config import REGISTER_DISABLED
import listdiffer
""" """
Flask Restful endpoints Flask Restful endpoints
@ -20,6 +22,9 @@ __copyright__ = "Copyright 2020, videON Team"
__module_name__ = "resources" __module_name__ = "resources"
__version__text__ = "1" __version__text__ = "1"
INVALID_JSON_SCHEMA_MSG = "invalid json schema"
DB_ERROR_MSG = "db session error"
class SignupApi(Resource): class SignupApi(Resource):
""" """
@ -39,18 +44,17 @@ class SignupApi(Resource):
userobj = self.userschema.load(body) userobj = self.userschema.load(body)
except Exception as e: except Exception as e:
current_app.logger.warning(e) current_app.logger.warning(e)
abort(417, "invalid json schema") abort(417, INVALID_JSON_SCHEMA_MSG)
user = User(name=userobj['name'], password=userobj['password']) user = VideonUser(name=userobj['name'], password=userobj['password'])
try: try:
user.hash_password() user.hash_password()
db.session.add(user) db.session.add(user)
db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback()
current_app.logger.warning(e) current_app.logger.warning(e)
abort(503, "user already exists") abort(503, "user already exists")
db.session.commit()
return self.usermetadataschema.dump(user), 200 return self.usermetadataschema.dump(user), 200
@ -69,9 +73,9 @@ class LoginApi(Resource):
userobj = self.userschema.load(body) userobj = self.userschema.load(body)
except Exception as e: except Exception as e:
current_app.logger.warning(e) current_app.logger.warning(e)
abort(417, "invalid json schema") abort(417, INVALID_JSON_SCHEMA_MSG)
user = User.query.filter_by(name=userobj['name']).first_or_404() user = VideonUser.query.filter_by(name=userobj['name']).first_or_404()
authorized = user.check_password(userobj['password']) authorized = user.check_password(userobj['password'])
if not authorized: if not authorized:
abort(401, "username or password incorrect") abort(401, "username or password incorrect")
@ -79,14 +83,13 @@ class LoginApi(Resource):
try: try:
user.last_logon = datetime.datetime.now() user.last_logon = datetime.datetime.now()
db.session.add(user) db.session.add(user)
db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback()
current_app.logger.exception(e) current_app.logger.exception(e)
abort(503, "db session error") abort(503, DB_ERROR_MSG)
expires = datetime.timedelta(days=7) expires = datetime.timedelta(days=7)
access_token = create_access_token(identity=str(user.name), expires_delta=expires) access_token = create_access_token(identity=str(user.name), expires_delta=expires)
db.session.commit()
return {'token': access_token}, 200 return {'token': access_token}, 200
@ -97,7 +100,7 @@ class UsersApi(Resource):
usermetadataschema = UserMetadataSchema(many=True) usermetadataschema = UserMetadataSchema(many=True)
def get(self): def get(self):
users = User.query.all() users = VideonUser.query.all()
return self.usermetadataschema.dump(users), 200 return self.usermetadataschema.dump(users), 200
@ -111,7 +114,7 @@ class UserParameterApi(Resource):
:param username: Username of user (url parameter) :param username: Username of user (url parameter)
:return: :return:
""" """
user = User.query.filter_by(name=username).first_or_404() user = VideonUser.query.filter_by(name=username).first_or_404()
return self.usermetadataschema.dump(user), 200 return self.usermetadataschema.dump(user), 200
def delete(self, username: str): def delete(self, username: str):
@ -120,16 +123,15 @@ class UserParameterApi(Resource):
:param username: Username of user (url parameter) :param username: Username of user (url parameter)
:return: :return:
""" """
user = User.query.filter_by(name=username).first_or_404() user = VideonUser.query.filter_by(name=username).first_or_404()
try: try:
db.session.delete(user) db.session.delete(user)
db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback()
current_app.logger.exception(e) current_app.logger.exception(e)
abort(503, "db session error") abort(503, DB_ERROR_MSG)
db.session.commit()
return self.usermetadataschema.dump(user), 200 return self.usermetadataschema.dump(user), 200
def put(self, username: str): def put(self, username: str):
@ -139,22 +141,21 @@ class UserParameterApi(Resource):
:return: :return:
""" """
body = request.get_json() body = request.get_json()
user = User.query.filter_by(name=username).first_or_404() user = VideonUser.query.filter_by(name=username).first_or_404()
try: try:
userobj = self.userschema.load(body) userobj = self.userschema.load(body)
except Exception as e: except Exception as e:
current_app.logger.warning(e) current_app.logger.warning(e)
abort(417, "invalid json schema") abort(417, INVALID_JSON_SCHEMA_MSG)
try: try:
user.password = userobj['password'] user.password = userobj['password']
user.hash_password() user.hash_password()
db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback()
current_app.logger.exception(e) current_app.logger.exception(e)
abort(503, "db session error") abort(503, DB_ERROR_MSG)
db.session.commit()
return self.usermetadataschema.dump(user), 200 return self.usermetadataschema.dump(user), 200
@ -162,71 +163,352 @@ class CreateIngestResource(Resource):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createIngest See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createIngest
""" """
ingestinputschema = IngestInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def post(self): def post(self):
pass 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): class CreateRestreamResource(Resource):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createRestream See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createRestream
""" """
restreaminputschema = RestreamInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def post(self): def post(self):
pass 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): class CreateEncodeResource(Resource):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createEncode See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createEncode
""" """
encodeinputschema = EncodeInputSchema(many=False)
streamresourceschema = StreamResourceSchema(many=False)
def post(self): def post(self):
pass 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): class GetAllStreamResources(Resource):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getResource See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getResource
""" """
streamresourceschema = StreamResourceSchema(many=True)
def get(self): def get(self):
pass streamreousrces = StreamResource.query.all()
return self.streamresourceschema.dump(streamreousrces), 200
class ManipulateStreamResource(Resource): class ManipulateStreamResource(Resource):
def get(self, resourceid: uuid4): streamresourceschema = StreamResourceSchema(many=False)
def get(self, resourceid: str):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getAResource See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getAResource
:param resourceid: :param resourceid:
:return: :return:
""" """
pass streamreousrce = StreamResource.query.filter_by(id=UUID(resourceid)).first_or_404()
return self.streamresourceschema.dump(streamreousrce), 200
def delete(self, resourceid: uuid4): def delete(self, resourceid: str):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/deleteResource See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/deleteResource
:param resourceid: :param resourceid:
:return: :return:
""" """
pass 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): class ModifyIngressResource(Resource):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editIngress See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editIngress
""" """
def put(self, resourceid: uuid4): ingestinputschema = IngestInputSchema(many=False)
pass 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): class ModifyRestreamResource(Resource):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editRestream See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editRestream
""" """
def put(self, resourceid: uuid4): restreaminputschema = RestreamInputSchema(many=False)
pass 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): class ModifyEncodeResource(Resource):
""" """
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editEncode See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editEncode
""" """
def put(self, resourceid: uuid4): encodeinputschema = EncodeInputSchema(many=False)
pass 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

View File

@ -1,8 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from flask_marshmallow.sqla import auto_field from flask_marshmallow.sqla import auto_field
from marshmallow_enum import EnumField
from models import User, StreamResource from models import VideonUser, StreamResource, StreamResourceTypeEnum, OutputUrls
from marshm import ma from marshm import ma
from db import db
from marshmallow import fields from marshmallow import fields
@ -33,13 +35,17 @@ class UserMetadataSchema(ma.SQLAlchemyAutoSchema):
Marshmallow schema generated Marshmallow schema generated
""" """
class Meta: class Meta:
model = User model = VideonUser
exclude = ('timestamp', 'password', 'id',) exclude = ('timestamp', 'password', 'id',)
sqla_session = db.session
creation_date = auto_field("timestamp", dump_only=False) creation_date = auto_field("timestamp", dump_only=False)
streamresources = auto_field
class IngestInputSchema(ma.Schema): class IngestInputSchema(ma.Schema):
outputNeighbours = fields.List(fields.UUID(), required=True) outputNeighbours = fields.List(fields.UUID(), required=False)
x = fields.Integer(required=True)
y = fields.Integer(required=True)
class EncodeInputSchema(ma.Schema): class EncodeInputSchema(ma.Schema):
@ -48,11 +54,35 @@ class EncodeInputSchema(ma.Schema):
bitrate = fields.Integer(required=False) bitrate = fields.Integer(required=False)
width = fields.Integer(required=False) width = fields.Integer(required=False)
height = fields.Integer(required=False) height = fields.Integer(required=False)
x = fields.Integer(required=True)
y = fields.Integer(required=True)
class RestreamInputSchema(ma.Schema): class RestreamInputSchema(ma.Schema):
inputNeighbour = fields.UUID(required=True) inputNeighbour = fields.UUID(required=False)
outputURLs = fields.List(fields.String(), required=True) outputURLs = fields.List(fields.String(), required=True)
x = fields.Integer(required=True)
y = fields.Integer(required=True)
class OutputUrlSchema(ma.SQLAlchemyAutoSchema):
"""
Marshmallow schema generated
"""
class Meta:
model = OutputUrls
fields = ('output_url', )
sqla_session = db.session
class StreamResourceIdSchema(ma.SQLAlchemyAutoSchema):
"""
Marshmallow schema generated
"""
class Meta:
model = StreamResource
fields = ('id',)
sqla_session = db.session
class StreamResourceSchema(ma.SQLAlchemyAutoSchema): class StreamResourceSchema(ma.SQLAlchemyAutoSchema):
@ -61,7 +91,9 @@ class StreamResourceSchema(ma.SQLAlchemyAutoSchema):
""" """
class Meta: class Meta:
model = StreamResource model = StreamResource
exclude = ('output_urls', 'parent_id', 'children',) exclude = ('parent_id',)
outputURLs = auto_field('output_urls', dump_only=False) sqla_session = db.session
inputNeighbour = auto_field('parent_id', dump_only=False) resource_type = EnumField(StreamResourceTypeEnum, by_value=True, dump_only=True)
outputNeighbours = auto_field('children', dump_only=False) output_urls = ma.Pluck(OutputUrlSchema, field_name='output_url', many=True, dump_only=True)
inputNeighbour = auto_field('parent_id', dump_only=True)
children = ma.Pluck(StreamResourceIdSchema, field_name='id', many=True, dump_only=True)