added state api

This commit is contained in:
2020-02-24 16:00:41 +01:00
parent 6ba38cd42d
commit 1d011af64b
10 changed files with 74 additions and 80 deletions

View File

@@ -7,7 +7,6 @@ name = "pypi"
ffmpeg-python = "*"
flask = "*"
flask-httpauth = "*"
flask-restplus-patched = "*"
flask-sqlalchemy = "*"
flask-login = "*"
pyjwt = "*"

45
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "381ef185977217922c32d4e8e182c1f885842e963576323cad0ffd6a59f1a5f8"
"sha256": "a58778e12f8a5bcff82fd344f97bf22ed1917412e66289d13be718fd88cfc64a"
},
"pipfile-spec": 6,
"requires": {
@@ -29,13 +29,6 @@
],
"version": "==8.0.0"
},
"apispec": {
"hashes": [
"sha256:419d0564b899e182c2af50483ea074db8cb05fee60838be58bb4542095d5c08d",
"sha256:9bf4e51d56c9067c60668b78210ae213894f060f85593dc2ad8805eb7d875a2a"
],
"version": "==3.3.0"
},
"apscheduler": {
"hashes": [
"sha256:3bb5229eed6fbbdafc13ce962712ae66e175aa214c69bed35a06bffcf0c5e244",
@@ -281,13 +274,6 @@
"index": "pypi",
"version": "==0.5.0"
},
"flask-marshmallow": {
"hashes": [
"sha256:01520ef1851ccb64d4ffb33196cddff895cc1302ae1585bff1abf58684a8111a",
"sha256:28b969193958d9602ab5d6add6d280e0e360c8e373d3492c2f73b024ecd36374"
],
"version": "==0.11.0"
},
"flask-migrate": {
"hashes": [
"sha256:6fb038be63d4c60727d5dfa5f581a6189af5b4e2925bc378697b4f0a40cfb4e1",
@@ -303,20 +289,6 @@
"index": "pypi",
"version": "==3.2.0"
},
"flask-restplus": {
"hashes": [
"sha256:a15d251923a8feb09a5d805c2f4d188555910a42c64d58f7dd281b8cac095f1b",
"sha256:a66e442d0bca08f389fc3d07b4d808fc89961285d12fb8013f7cf15516fa9f5c"
],
"version": "==0.13.0"
},
"flask-restplus-patched": {
"hashes": [
"sha256:36342775f9e0990dfc000dbe61133dfe56f9ef32c9b4c6293ba7f2c128d16efc"
],
"index": "pypi",
"version": "==0.1.10"
},
"flask-restx": {
"hashes": [
"sha256:641759fe7cba1cb073d15b4258b1b15840af8cffe6edbd0da3e6b61eae0a67a6",
@@ -489,13 +461,6 @@
],
"version": "==1.1.1"
},
"marshmallow": {
"hashes": [
"sha256:3a94945a7461f2ab4df9576e51c97d66bee2c86155d3d3933fab752b31effab8",
"sha256:4b95c7735f93eb781dfdc4dded028108998cad759dda8dd9d4b5b4ac574cbf13"
],
"version": "==3.5.0"
},
"monotonic": {
"hashes": [
"sha256:23953d55076df038541e648a53676fb24980f7a1be290cdda21300b3bc21dfb0",
@@ -843,14 +808,6 @@
],
"version": "==1.25.8"
},
"webargs": {
"hashes": [
"sha256:4f04918864c7602886335d8099f9b8960ee698b6b914f022736ed50be6b71235",
"sha256:871642a2e0c62f21d5b78f357750ac7a87e6bc734c972f633aa5fb6204fbf29a",
"sha256:fc81c9f9d391acfbce406a319217319fd8b2fd862f7fdb5319ad06944f36ed25"
],
"version": "==5.5.3"
},
"webencodings": {
"hashes": [
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",

View File

@@ -30,6 +30,7 @@ api_group = Namespace('group', description="Group management namespace", authori
api_permissions = Namespace('permissions', description="Permissions management namespace", authorizations=api_authorizations)
api_room = Namespace('room', description="Room management namespace", authorizations=api_authorizations)
api_recorder = Namespace('recorder', description="Recorder management namespace", authorizations=api_authorizations)
api_state = Namespace('state', description="Object state management namespace", authorizations=api_authorizations)
api_virtual_command = Namespace('virtual_command', description="Virtual command namespace",
authorizations=api_authorizations)
api_cron_job = Namespace('cron_job', description="Cron job namespace",
@@ -42,6 +43,7 @@ api_v1.add_namespace(api_group)
api_v1.add_namespace(api_permissions)
api_v1.add_namespace(api_room)
api_v1.add_namespace(api_recorder)
api_v1.add_namespace(api_state)
api_v1.add_namespace(api_virtual_command)
api_v1.add_namespace(api_cron_job)
api_v1.add_namespace(api_control)
@@ -64,6 +66,7 @@ from .permission_api import *
from .group_api import *
from .room_api import *
from .recorder_api import *
from .state_api import *
from .control_api import *
from .virtual_command_api import *

View File

@@ -1,5 +1,5 @@
from flask_restx import fields
from backend.api import api_user, api_recorder, api_v1
from backend.api import api_user, api_recorder, api_v1, api_state
generic_id_parser = api_v1.parser()
generic_id_parser.add_argument('id', type=str, required=True, store_missing=False)
@@ -102,3 +102,6 @@ recorder_model_model = api_recorder.model('Recorder Model', {
description='Model of the recorder.'),
'commands': fields.List(fields.Nested(recorder_command_model), attribute="recorder_commands")
})
state_model = api_state.model('Recorder', {
})

View File

@@ -1,9 +1,6 @@
# Copyright (c) 2019. Tobias Kurze
"""
This module provides functions related to authentication through the API.
For example: listing of available auth providers or registration of users.
Login through API does not start a new session, but instead returns JWT.
This module provides functions related to recorders through the API.
"""
from datetime import datetime
from pprint import pprint

48
backend/api/state_api.py Normal file
View File

@@ -0,0 +1,48 @@
# Copyright (c) 2019. Tobias Kurze
"""
This module provides functions related to object states through the API.
"""
from datetime import datetime
from pprint import pprint
from flask_jwt_extended import jwt_required
from flask_restx import fields, Resource, inputs
from backend import db, app
from backend.api import api_state
from backend.api.models import recorder_model, recorder_model_model, recorder_command_model, state_model
from backend.models.recorder_model import Recorder, RecorderModel, RecorderCommand
from backend.models.room_model import Room
import backend.recorder_adapters as r_a
# ==
@api_state.route('/<int:id>')
@api_state.response(404, 'Recorder not found')
@api_state.param('id', 'The recorder identifier')
class RecorderStateResource(Resource):
@jwt_required
@api_state.doc('get_recorder_state')
@api_state.marshal_with(state_model, skip_none=False)
def get(self, id):
"""Fetch a recorder given its identifier"""
recorder = Recorder.query.get(id)
if recorder is None:
api_state.abort(404)
return recorder
@api_state.route('')
class RecorderStateList(Resource):
@jwt_required
@api_state.doc('recorders')
@api_state.marshal_list_with(recorder_model, skip_none=False)
def get(self):
"""
List all recorders
:return: recorders
"""
return Recorder.get_all()

View File

@@ -47,6 +47,9 @@ def add_default_jobs(sched=None, testing=False):
check_recorder_state_job = sched.add_job(recorder_checker.check_object_state, 'interval', minutes=2,
id="check_recorder_state_job")
"""
Job regularly sending the state to "frontend recorders" through websocket
"""
send_update_state_to_recorder_job = sched.add_job(
lambda: send_state_update_to_recorders(recorder_checker.get_current_state()), 'interval', minutes=1,
id="send_update_state_to_recorder_job")

View File

@@ -22,6 +22,11 @@ T = TypeVar('T')
class StateChecker(Generic[T]):
"""
This class is designed generically to regularly check the state of objects with given function(s).
The determined state is stored "locally" in the state checker object and NOT reflected back to the checked objects!
It can be retrieved by calling get_current_state.
"""
def __init__(self, state_checker_func: Union[Callable, List[Callable]], type_to_check: T, type_name=None,
threads=NUM_THREADS):
self.num_threads = threads
@@ -135,7 +140,6 @@ class StateChecker(Generic[T]):
'previous': {'state_ok': self.state_results[o_s]['state_ok'],
'msg': self.state_results[o_s].get('msg', None),
'time_stamp': self.state_results[o_s].get('time_stamp', None)}}
pass
else:
self.state_results[o_s] = object_states[o_s]

View File

@@ -38,8 +38,6 @@ rec_err_state_log_stream_handler.setLevel(logging.WARNING)
logger.addHandler(rec_err_state_log_stream_handler)
#logger.addHandler(mem_handler)
base_url = "https://opencast.bibliothek.kit.edu"
session = requests.session()
session.auth = HTTPBasicAuth(Config.OPENCAST_USER, Config.OPENCAST_PW)
@@ -55,7 +53,7 @@ def get_service_url(service_type: str):
if service_type in config['service_urls']:
return config['service_urls'][service_type]
params = {'serviceType': service_type}
url = base_url + "/services/available.json"
url = Config.OPENCAST_URL + "/services/available.json"
res = session.get(url, params=params)
if res.ok:
service = res.json()["services"]["service"]
@@ -135,6 +133,7 @@ def check_stream_sanity(recorder: Recorder, recorder_adapter: RecorderAdapter):
for i in range(0, Config.STREAM_SANITY_CHECK_RETRIES):
do_checks = False
if do_checks:
return True
else:
@@ -224,7 +223,6 @@ if __name__ == '__main__':
for a in agents:
agent_states[a.get('name')] = 'PROBLEMATIC - unknown'
# pool = ThreadPool(5)
# pool.map(check_capture_agent_state, agents)
@@ -239,7 +237,6 @@ if __name__ == '__main__':
except TimeoutError as e:
logger.error("Timeout while pinging capture agent! {}".format(e))
with ThreadPool(NUM_THREADS) as pool:
results = [pool.apply_async(check_capture_agent_state, (agent,)) for agent in agents]
try:

View File

@@ -9,29 +9,6 @@ from pydub import AudioSegment
from pydub.playback import play
def old_test():
file_name = tempfile.gettempdir() + os.path.sep + "test.jpg"
print(file_name)
if os.path.exists(file_name):
os.remove(file_name)
process = (
ffmpeg
.input('rtsp://172.22.246.207/extron1')
# .input('rtsp://172.22.246.207/extron3')
.output(file_name, vframes=1)
# .output('-', format='h264')
.run(capture_stdout=True)
)
image = Image.open(file_name)
r, g, b = image.split()
print(r.histogram())
print(g.histogram())
print(b.histogram())
image.show()
# old_test()
def is_single_color_image(image):
single_color_image = True
color = {}
@@ -97,7 +74,7 @@ def check_if_audio_is_valid(stream_url, sample_length_sec=3, lower_alert_limit_d
sound = AudioSegment.from_file(file_name, "aac")
# print(sound.dBFS)
play(sound)
#play(sound)
if sound.max_dBFS == -float('inf'):
return False, "No active audio signal detected!"
elif sound.max_dBFS < lower_alert_limit_dBFS:
@@ -115,9 +92,14 @@ def check_if_audio_is_valid(stream_url, sample_length_sec=3, lower_alert_limit_d
else:
return False, msg
print(check_if_audio_is_valid('rtsp://172.22.246.207/extron1'))
"""
Following code is not working correctly - ffmpeg parameters are wrong.
"""
"""
def check_if_audio_is_valid_stream(stream_url, raise_errors=True):
audio, _ = (
ffmpeg
@@ -130,3 +112,4 @@ def check_if_audio_is_valid_stream(stream_url, raise_errors=True):
play(sound)
# check_if_audio_is_valid('rtsp://172.22.246.207/extron1')
"""