diff --git a/backend/__init__.py b/backend/__init__.py index b37b2c8..97f8753 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -12,11 +12,25 @@ from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS + +class LrcException(Exception): + def __init__(self, message: str, html_code: int = None): + super().__init__(message) + self.html_code = html_code + + def __repr__(self): + if self.html_code is not None: + return "LRC Exception: \"{}\" (HTML Code: {})".format(', '.join(super().args), self.html_code) + return ', '.join(super().args) + + def __str__(self): + return self.__repr__() + + app = Flask(__name__) app.config.from_object('backend.config.Config') db = SQLAlchemy(app) - login_manager = LoginManager() login_manager.init_app(app) @@ -42,6 +56,7 @@ def verify_token(token): app.logger.info(decoded) return True + basic_auth = HTTPBasicAuth() multi_auth = MultiAuth(basic_auth, jwt_auth) @@ -67,4 +82,3 @@ CORS(api_bp) # Fix flask-restplus by duck typing error handlers jwt_extended._set_error_handler_callbacks(api_v1) - diff --git a/backend/models/initial_recorders.json b/backend/models/initial_recorders.json new file mode 100644 index 0000000..8610b8c --- /dev/null +++ b/backend/models/initial_recorders.json @@ -0,0 +1,204 @@ +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 diff --git a/backend/models/recorder_model.py b/backend/models/recorder_model.py index 42a646e..7947abb 100644 --- a/backend/models/recorder_model.py +++ b/backend/models/recorder_model.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + # -*- coding: utf-8 -*- """ Models for lecture recorder """ diff --git a/backend/recorder_adapters/__init__.py b/backend/recorder_adapters/__init__.py index 769e8b3..57a029d 100644 --- a/backend/recorder_adapters/__init__.py +++ b/backend/recorder_adapters/__init__.py @@ -100,6 +100,10 @@ class RecorderAdapter: def _get_version(self): pass + @abstractmethod + def is_recording(self) -> bool: + pass + def get_defined_recorder_adapters(): models = [] diff --git a/backend/recorder_adapters/epiphan_base.py b/backend/recorder_adapters/epiphan_base.py new file mode 100644 index 0000000..4e62347 --- /dev/null +++ b/backend/recorder_adapters/epiphan_base.py @@ -0,0 +1,142 @@ +# Copyright (c) 2019. Tobias Kurze +import shutil +import time +from datetime import datetime +from pprint import pprint + +import requests +from requests.auth import HTTPBasicAuth + +from backend import LrcException +from backend.recorder_adapters import RecorderAdapter + +# HOST = "localhost" +BASE_URL = "http://172.23.8.102" # Audimax SMP 351 + +USER = "admin" +PW = "lrgrashof+-" + + +class EpiphanV1(RecorderAdapter): + + def __init__(self, admin_user: str, admin_pw: str): + self.user = admin_user + self.password = admin_pw + self.session = requests.Session() + self.session.auth = HTTPBasicAuth(self.user, self.password) + + def _get_name(self): + pass + + def _get_version(self): + pass + + def get_status(self) -> dict: + res = self.session.get(BASE_URL + "/admin/ajax/recorder_status.cgi") + if res.ok: + return res.json() + raise LrcException(res.text, res.status_code) + + def get_sysinfo(self) -> dict: + res = self.session.get(BASE_URL + "/ajax/sysinfo.cgi") + if res.ok: + return res.json() + raise LrcException(res.text, res.status_code) + + def is_recording(self) -> bool: + state = self.get_status().get('state', None) + return state == "up" + + def get_recording_time(self): + """ + Returns recording time in seconds. Also returns 0 if not recording. + :return: + """ + return self.get_status().get('seconds', None) + + def start_recording(self): + res = self.session.get(BASE_URL + "/admin/ajax/start_recorder.cgi") + if not res.ok: + raise LrcException(res.text, res.status_code) + time.sleep(2) # just a little bit of waiting time -> it takes a bit for the Epiphan to update its state + + def stop_recording(self): + res = self.session.get(BASE_URL + "/admin/ajax/stop_recorder.cgi") + if not res.ok: + raise LrcException(res.text, res.status_code) + time.sleep(4) # just a little bit of waiting time -> it takes a bit for the Epiphan to update its state + + def get_ip_address(self): + try: + return self.get_sysinfo().get("system").get("network").get("interfaces")[0].get('ipaddr', None) + except Exception as err: + raise LrcException(str(err)) + + def get_disk_space(self): + try: + data = self.get_sysinfo().get("system").get("data") + return {'available': data.get("available", None), 'free': data.get("free", None), + 'total': data.get("total", None), 'used': data.get("total", 0) - data.get("available", 0)} + except Exception as err: + raise LrcException(str(err)) + + def get_video_inputs(self) -> list: + ret = [] + try: + video = self.get_sysinfo().get("inputs").get("video") + for v in video: + ret.append( + {'id': v.get('id', None), 'name': v.get('name', None), 'resolution': v.get('resolution', None)}) + return ret + except Exception as err: + raise LrcException(str(err)) + + def get_hardware_revision(self): + try: + return self.get_sysinfo().get("system").get("firmware") + except Exception as err: + raise LrcException(str(err)) + + def get_system_time(self): + try: + time_stamp = self.get_sysinfo().get("time") + return {'unix_time_stamp': time_stamp, + 'date_time_utc': datetime.utcfromtimestamp(time_stamp).strftime('%Y-%m-%dT%H:%M:%SZ')} + except Exception as err: + raise LrcException(str(err)) + + def get_screenshot(self): + ret = self.session.get(BASE_URL + "/admin/grab_frame.cgi?size=256x192&device=DAV93133.vga&_t=1573471990578", + stream=True) + + print(ret) + pprint(ret.headers) + with open('out.jpg', 'wb') as out_file: + shutil.copyfileobj(ret.raw, out_file) + del ret + + +if __name__ == '__main__': + e = EpiphanV1(USER, PW) + try: + # print(e.is_recording()) + """ + if e.is_recording(): + e.stop_recording() + else: + e.start_recording() + print(e.is_recording()) + """ + + """ + print(e.get_ip_address()) + print(e.get_disk_space()) + print(e.get_video_inputs()) + print(e.get_hardware_revision()) + print(e.get_system_time()) + """ + + e.get_screenshot() + + except LrcException as e: + print(e) diff --git a/backend/recorder_adapters/extron_smp.py b/backend/recorder_adapters/extron_smp.py index 28e94f0..a736545 100644 --- a/backend/recorder_adapters/extron_smp.py +++ b/backend/recorder_adapters/extron_smp.py @@ -6,13 +6,13 @@ REQUIRES_USER = False REQUIRES_PW = True # HOST = "localhost" -HOST = "129.13.51.102" # Audimax SMP 351 +# HOST = "129.13.51.102" # Audimax SMP 351 # HOST = "129.13.51.106" # Tulla SMP 351 -# HOST = "172.22.246.207" # Test SMP MZ +HOST = "172.22.246.207" # Test SMP MZ USER = "admin" PW = "123mzsmp" -PW = "audimaxsmp" +#PW = "audimaxsmp" class SMP(TelnetAdapter, RecorderAdapter): @@ -701,6 +701,9 @@ class SMP(TelnetAdapter, RecorderAdapter): some advanced options skipped """ + def is_recording(self) -> bool: + pass + def get_input_hdcp_status(self, input_number: int): """ returns: diff --git a/backend/tools/import_excel_recorder_list.py b/backend/tools/import_excel_recorder_list.py new file mode 100644 index 0000000..db78551 --- /dev/null +++ b/backend/tools/import_excel_recorder_list.py @@ -0,0 +1,55 @@ +import json +from pprint import pprint + +import xlrd + +MAX_CONSECUTIVE_EMPTY_ROWS_BEFORE_CANCEL = 5 +file_location = "/home/tobias/tmp/Hoersaal-Rec-Liste.xlsx" + +wb = xlrd.open_workbook(file_location) +sheet = wb.sheet_by_index(0) + +headers = None +recorders = [] +a_lot_of_empty_rows = False +consecutive_empty_rows = 0 +ix = 0 +while not a_lot_of_empty_rows: + try: + vals = sheet.row_values(ix) + except IndexError: + break + if len(set(vals)) == 1 and set(vals).pop() == '': + consecutive_empty_rows += 1 + if consecutive_empty_rows > MAX_CONSECUTIVE_EMPTY_ROWS_BEFORE_CANCEL: + a_lot_of_empty_rows = True + else: + consecutive_empty_rows = 0 + if len(set(vals)) > 5 and headers is None: # get table header + headers = {ix: vals[ix] for ix in range(0, len(vals))} + elif len(set(vals)) > 5: # regular rows + recorders.append({headers[ix]: vals[ix] for ix in range(0, len(vals))}) + ix += 1 + if ix >= 100: + a_lot_of_empty_rows = True + +#pprint(recorders) +print(len(recorders)) + +nicely_formatted_recorders = [] +for r in recorders: + rec = {'name': r['Opencast / CM'], + 'building': r['Gebäude'], + 'room': r['Hörsaal'], + 'username': r['Benutzer'], + 'password': r['PW'], + 'ip': r['IP'], + 'mac': r['MAC'], + 'type': 'SMP 351' if 'SMP 351' in r['Rekorder-Typ'] else r['Rekorder-Typ'], + 'additional_camera': r['Zus. Kamera'] == 'Ja', + 'firmware_version': r['FW – Vers.'], + 'description': json.dumps( + {'comment': r['Bemerkung'], 'network_port': r['Dosennummer'], 'rsync_name': r['Rsync-Name']})} + nicely_formatted_recorders.append(rec) + +print(json.dumps(nicely_formatted_recorders)) diff --git a/backend/tools/simple_state_checker.py b/backend/tools/simple_state_checker.py new file mode 100644 index 0000000..19721b0 --- /dev/null +++ b/backend/tools/simple_state_checker.py @@ -0,0 +1,57 @@ +from pprint import pprint + +import requests +from requests.auth import HTTPBasicAuth +from ics import Calendar + +base_url = "https://opencast.bibliothek.kit.edu/recordings/calendars?agentid=CS+30.46+Chemie+Neuer+Hoersaal" +base_url = "https://opencast.bibliothek.kit.edu" + +session = requests.session() +session.auth = HTTPBasicAuth("admin", "mz.paziuw!") + +config = {'service_urls': {}} + + +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" + res = session.get(url, params=params) + if res.ok: + service = res.json()["services"]["service"] + config["service_urls"][service_type] = service["host"] + \ + service["path"] + return service["host"] +service["path"] + return None + + +def get_calender(rec_id): + params = {'agentid': rec_id} + url = get_service_url('org.opencastproject.scheduler') + "/calendars" + print(url) + res = session.get(url, params=params) + if res.ok: + return Calendar(res.text) + + +def get_capture_agents(): + url = get_service_url("org.opencastproject.capture.admin") + "/agents.json" + res = session.get(url) + if res.ok: + return res.json()["agents"]["agent"] + +agents = get_capture_agents() +for a in agents: + print(a['name']) + print(a['state']) + if a['state'] == "capturing": + c = get_calender(a['name']) + print(list(c.timeline.now())) +exit() + +c = get_calender('CS 30.46 Chemie Neuer Hoersaal') +print(c.events) +print(list(c.timeline)) # chronological order +print(list(c.timeline.now()))