Import stateserver into tree, MyPy-ify, CI-ify
This commit is contained in:
parent
de49a1d6be
commit
45f26d7c75
8 changed files with 469 additions and 42 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -27,7 +27,8 @@ yarn-error.log*
|
||||||
|
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
env/
|
env/
|
||||||
|
env.ci/
|
||||||
|
|
||||||
shittyserver.ini
|
serverconfig.ini
|
||||||
|
|
||||||
.idea/
|
.idea/
|
12
Jenkinsfile
vendored
12
Jenkinsfile
vendored
|
@ -29,9 +29,14 @@ pipeline {
|
||||||
sh 'node_modules/.bin/tsc -p tsconfig.json --noEmit --extendedDiagnostics'
|
sh 'node_modules/.bin/tsc -p tsconfig.json --noEmit --extendedDiagnostics'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('MyPy') {
|
stage('MyPy (stateserver)') {
|
||||||
steps {
|
steps {
|
||||||
sh 'env/bin/mypy server.py'
|
sh 'env/bin/mypy stateserver.py'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('MyPy (shittyserver)') {
|
||||||
|
steps {
|
||||||
|
sh 'env/bin/mypy shittyserver.py'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +74,8 @@ pipeline {
|
||||||
stage('Deploy server') {
|
stage('Deploy server') {
|
||||||
steps {
|
steps {
|
||||||
sshagent(credentials: ['ury']) {
|
sshagent(credentials: ['ury']) {
|
||||||
sh 'scp -v -o StrictHostKeyChecking=no server.py liquidsoap@dolby.ury:/opt/webstudioserver/server.py'
|
sh 'scp -v -o StrictHostKeyChecking=no stateserver.py liquidsoap@dolby.ury:/opt/webstudioserver/stateserver.py'
|
||||||
|
sh 'scp -v -o StrictHostKeyChecking=no shittyserver.py liquidsoap@dolby.ury:/opt/webstudioserver/shittyserver.py'
|
||||||
sh 'scp -v -o StrictHostKeyChecking=no requirements.txt liquidsoap@dolby.ury:/opt/webstudioserver/requirements.txt'
|
sh 'scp -v -o StrictHostKeyChecking=no requirements.txt liquidsoap@dolby.ury:/opt/webstudioserver/requirements.txt'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,59 @@
|
||||||
aiohttp==3.6.2
|
appdirs==1.4.3
|
||||||
async-timeout==3.0.1
|
astroid==2.3.3
|
||||||
attrs==19.3.0
|
attrs==19.3.0
|
||||||
|
autopep8==1.5
|
||||||
|
black==19.10b0
|
||||||
|
certifi==2019.11.28
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
idna==2.9
|
click==7.1.1
|
||||||
multidict==4.7.5
|
colorama==0.4.1
|
||||||
mypy==0.770
|
decorator==4.4.0
|
||||||
|
distlib==0.3.0
|
||||||
|
docopt==0.6.2
|
||||||
|
dokuwiki==1.2.1
|
||||||
|
entrypoints==0.3
|
||||||
|
filelock==3.0.12
|
||||||
|
flake8==3.7.9
|
||||||
|
frida==12.8.7
|
||||||
|
frida-tools==6.0.0
|
||||||
|
greenlet==0.4.15
|
||||||
|
idna==2.8
|
||||||
|
isort==4.3.21
|
||||||
|
jedi==0.15.2
|
||||||
|
lazy-object-proxy==1.4.3
|
||||||
|
mccabe==0.6.1
|
||||||
|
msgpack==1.0.0
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==0.4.3
|
||||||
|
parso==0.6.2
|
||||||
|
pathspec==0.7.0
|
||||||
|
pipenv==2018.11.26
|
||||||
|
pluggy==0.13.1
|
||||||
|
prompt-toolkit==2.0.10
|
||||||
|
protonvpn-cli==2.2.1
|
||||||
|
psutil==5.6.3
|
||||||
|
pycodestyle==2.5.0
|
||||||
|
pydocstyle==5.0.2
|
||||||
|
pyflakes==2.1.1
|
||||||
|
Pygments==2.5.2
|
||||||
|
pylint==2.4.4
|
||||||
|
pynvim==0.4.1
|
||||||
|
pyte==0.8.0
|
||||||
|
python-jsonrpc-server==0.3.4
|
||||||
|
python-language-server==0.31.8
|
||||||
|
pythondialog==3.5.1
|
||||||
|
regex==2020.2.20
|
||||||
|
requests==2.22.0
|
||||||
|
rope==0.16.0
|
||||||
|
six==1.12.0
|
||||||
|
snowballstemmer==2.0.0
|
||||||
|
thefuck==3.29
|
||||||
|
toml==0.10.0
|
||||||
typed-ast==1.4.1
|
typed-ast==1.4.1
|
||||||
typing-extensions==3.7.4.2
|
typing-extensions==3.7.4.2
|
||||||
websockets==8.1
|
ujson==1.35
|
||||||
yarl==1.4.2
|
urllib3==1.25.8
|
||||||
|
virtualenv==20.0.16
|
||||||
|
virtualenv-clone==0.5.4
|
||||||
|
wcwidth==0.1.7
|
||||||
|
wrapt==1.11.2
|
||||||
|
yapf==0.29.0
|
||||||
|
|
|
@ -8,12 +8,18 @@ blinker==1.4
|
||||||
certifi==2020.4.5.1
|
certifi==2020.4.5.1
|
||||||
cffi==1.14.0
|
cffi==1.14.0
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
|
click==7.1.1
|
||||||
crc32c==2.0
|
crc32c==2.0
|
||||||
cryptography==2.8
|
cryptography==2.8
|
||||||
|
Flask==1.1.2
|
||||||
|
Flask-Cors==3.0.8
|
||||||
idna==2.9
|
idna==2.9
|
||||||
|
itsdangerous==1.1.0
|
||||||
JACK-Client==0.5.2
|
JACK-Client==0.5.2
|
||||||
jedi==0.15.2
|
jedi==0.15.2
|
||||||
|
Jinja2==2.11.2
|
||||||
jsonpickle==1.3
|
jsonpickle==1.3
|
||||||
|
MarkupSafe==1.1.1
|
||||||
multidict==4.7.5
|
multidict==4.7.5
|
||||||
mypy==0.770
|
mypy==0.770
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==0.4.3
|
||||||
|
@ -35,4 +41,5 @@ typing-extensions==3.7.4.2
|
||||||
ujson==1.35
|
ujson==1.35
|
||||||
urllib3==1.25.8
|
urllib3==1.25.8
|
||||||
websockets==8.1
|
websockets==8.1
|
||||||
|
Werkzeug==1.0.1
|
||||||
yarl==1.4.2
|
yarl==1.4.2
|
||||||
|
|
12
serverconfig.ini.example
Normal file
12
serverconfig.ini.example
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[raygun]
|
||||||
|
key = CHANGEME
|
||||||
|
enable = False
|
||||||
|
|
||||||
|
[shittyserver]
|
||||||
|
notify_url = https://example.com
|
||||||
|
websocket_port = 8079
|
||||||
|
telnet_port = 8078
|
||||||
|
|
||||||
|
[stateserver]
|
||||||
|
myradio_key = CHANGEME
|
||||||
|
sustainer_autonews = True
|
|
@ -1,10 +0,0 @@
|
||||||
[raygun]
|
|
||||||
key = CHANGEME
|
|
||||||
enable = False
|
|
||||||
|
|
||||||
[mattserver]
|
|
||||||
notify_url = https://ent9s2r5u77vj.x.pipedream.net
|
|
||||||
|
|
||||||
[ports]
|
|
||||||
websocket = 8079
|
|
||||||
telnet = 8078
|
|
|
@ -1,27 +1,24 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import websockets
|
import configparser
|
||||||
import json
|
import json
|
||||||
import uuid
|
|
||||||
import av # type: ignore
|
|
||||||
import struct
|
|
||||||
from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription # type: ignore
|
|
||||||
from aiortc.mediastreams import MediaStreamError # type: ignore
|
|
||||||
from aiortc.contrib.media import MediaBlackhole, MediaPlayer # type: ignore
|
|
||||||
import jack as Jack # type: ignore
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
|
||||||
from typing import Optional, Any, Type, Dict
|
|
||||||
from types import TracebackType
|
|
||||||
import sys
|
import sys
|
||||||
import aiohttp
|
import uuid
|
||||||
from raygun4py import raygunprovider # type: ignore
|
from datetime import datetime
|
||||||
import struct
|
from types import TracebackType
|
||||||
|
from typing import Optional, Any, Type, Dict
|
||||||
|
|
||||||
import configparser
|
import aiohttp
|
||||||
|
import av # type: ignore
|
||||||
|
import jack as Jack # type: ignore
|
||||||
|
import websockets
|
||||||
|
from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription # type: ignore
|
||||||
|
from aiortc.mediastreams import MediaStreamError # type: ignore
|
||||||
|
from raygun4py import raygunprovider # type: ignore
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read("shittyserver.ini")
|
config.read("serverconfig.ini")
|
||||||
|
|
||||||
ENABLE_EXCEPTION_LOGGING = False
|
ENABLE_EXCEPTION_LOGGING = False
|
||||||
|
|
||||||
|
@ -112,7 +109,7 @@ async def notify_mattserver_about_sessions() -> None:
|
||||||
data: Dict[str, Dict[str, str]] = {}
|
data: Dict[str, Dict[str, str]] = {}
|
||||||
for sid, sess in active_sessions.items():
|
for sid, sess in active_sessions.items():
|
||||||
data[sid] = sess.to_dict()
|
data[sid] = sess.to_dict()
|
||||||
async with session.post(config.get("mattserver", "notify_url"), json=data) as response:
|
async with session.post(config.get("shittyserver", "notify_url"), json=data) as response:
|
||||||
print("Mattserver response", response)
|
print("Mattserver response", response)
|
||||||
|
|
||||||
|
|
||||||
|
@ -339,10 +336,10 @@ async def serve(websocket: websockets.WebSocketServerProtocol, path: str) -> Non
|
||||||
|
|
||||||
|
|
||||||
start_server = websockets.serve(
|
start_server = websockets.serve(
|
||||||
serve, "localhost", int(config.get("ports", "websocket"))
|
serve, "localhost", int(config.get("shittyserver", "websocket_port"))
|
||||||
)
|
)
|
||||||
|
|
||||||
print("Shittyserver WS starting on port {}.".format(config.get("ports", "websocket")))
|
print("Shittyserver WS starting on port {}.".format(config.get("shittyserver", "websocket_port")))
|
||||||
|
|
||||||
|
|
||||||
async def telnet_server(
|
async def telnet_server(
|
||||||
|
@ -417,14 +414,14 @@ async def telnet_server(
|
||||||
|
|
||||||
async def run_telnet_server() -> None:
|
async def run_telnet_server() -> None:
|
||||||
server = await asyncio.start_server(
|
server = await asyncio.start_server(
|
||||||
telnet_server, "localhost", int(config.get("ports", "telnet"))
|
telnet_server, "localhost", int(config.get("shittyserver", "telnet_port"))
|
||||||
)
|
)
|
||||||
await server.serve_forever()
|
await server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
jack.activate()
|
jack.activate()
|
||||||
|
|
||||||
print("Shittyserver TELNET starting on port {}".format(config.get("ports", "telnet")))
|
print("Shittyserver TELNET starting on port {}".format(config.get("shittyserver", "telnet_port")))
|
||||||
asyncio.get_event_loop().run_until_complete(notify_mattserver_about_sessions())
|
asyncio.get_event_loop().run_until_complete(notify_mattserver_about_sessions())
|
||||||
|
|
||||||
asyncio.get_event_loop().run_until_complete(
|
asyncio.get_event_loop().run_until_complete(
|
367
stateserver.py
Executable file
367
stateserver.py
Executable file
|
@ -0,0 +1,367 @@
|
||||||
|
#!flask/bin/python
|
||||||
|
## IMPORTANT ASSUMPTIONS MADE:
|
||||||
|
# show timestamps start on the hour.
|
||||||
|
# normal shows in real studios aren't currently a thing!!!
|
||||||
|
import subprocess
|
||||||
|
from typing import List, Any, Dict, Optional
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, request
|
||||||
|
from flask_cors import CORS # type: ignore
|
||||||
|
import requests
|
||||||
|
import datetime
|
||||||
|
import random
|
||||||
|
from telnetlib import Telnet
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read("serverconfig.ini")
|
||||||
|
|
||||||
|
api_key = config.get("stateserver", "myradio_key")
|
||||||
|
app = Flask(__name__)
|
||||||
|
CORS(app) # Enable Cors access-all
|
||||||
|
|
||||||
|
SUSTAINER_AUTONEWS = config.get("stateserver", "sustainer_autonews") == "True"
|
||||||
|
|
||||||
|
|
||||||
|
def do_ws_srv_telnet(source: str) -> None:
|
||||||
|
HOST = "localhost"
|
||||||
|
print("telnet {} {} SEL {}".format(HOST, config.get("shittyserver", "telnet_port"), source))
|
||||||
|
tn = Telnet(HOST, int(config.get("shittyserver", "telnet_port")))
|
||||||
|
tn.write(b"SEL " + str.encode(source) + b"\n")
|
||||||
|
try:
|
||||||
|
print(tn.read_until(b"\n").decode("utf-8"))
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
tn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def genFail(reason: str, code: int = 400) -> Any:
|
||||||
|
return jsonify({"status": "FAIL", "reason": reason})
|
||||||
|
|
||||||
|
|
||||||
|
def genPayload(payload: Any) -> Any:
|
||||||
|
return jsonify({"status": "OK", "payload": payload})
|
||||||
|
|
||||||
|
|
||||||
|
def myradioApiRequest(url: str) -> Any:
|
||||||
|
res = requests.get('https://ury.org.uk/api/v2/' + url + '?api_key=' + api_key)
|
||||||
|
if res.ok:
|
||||||
|
return res.json()["payload"]
|
||||||
|
else:
|
||||||
|
raise Exception("err {} {}".format(res.status_code, res.text))
|
||||||
|
|
||||||
|
|
||||||
|
def getNextHourTimestamp() -> int:
|
||||||
|
current = datetime.datetime.now()
|
||||||
|
currentPlusHour = current + datetime.timedelta(hours=1)
|
||||||
|
nextHourStart = currentPlusHour.replace(minute=0, second=0)
|
||||||
|
nextTimestamp = int(nextHourStart.timestamp())
|
||||||
|
return nextTimestamp
|
||||||
|
|
||||||
|
|
||||||
|
# sadly we're on python 3.7 so we can't use TypedDict
|
||||||
|
Connection = Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
def getConnByID(connID: str) -> Optional[Connection]:
|
||||||
|
for conn in connections:
|
||||||
|
if conn["connid"] == connID:
|
||||||
|
return conn
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SOURCE_JUKEBOX = 3 # Set to 8 for testing.
|
||||||
|
SOURCE_OB = 4
|
||||||
|
SOURCE_WS = 5
|
||||||
|
SOURCE_OFFAIR = 8
|
||||||
|
SOURCES = [SOURCE_JUKEBOX, SOURCE_OB, SOURCE_WS, SOURCE_OFFAIR]
|
||||||
|
|
||||||
|
# This array will only hold connections we've validated to be authorised to broadcast.
|
||||||
|
connections: List[Connection] = []
|
||||||
|
wsSessions: Dict[str, Dict[str, str]] = {}
|
||||||
|
lastConnectionIDToRegister = -1
|
||||||
|
|
||||||
|
|
||||||
|
def getCurrentShowConnection() -> Optional[Connection]:
|
||||||
|
for connection in connections:
|
||||||
|
if (connection["startTimestamp"] <= datetime.datetime.now().timestamp()) and (
|
||||||
|
connection["endTimestamp"] >= getNextHourTimestamp()):
|
||||||
|
return connection
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def getNextHourConnection() -> Optional[Connection]:
|
||||||
|
nextHourTimestamp = getNextHourTimestamp()
|
||||||
|
isConnectionEnding = False
|
||||||
|
for connection in connections:
|
||||||
|
if connection["startTimestamp"] == nextHourTimestamp:
|
||||||
|
return connection
|
||||||
|
if connection["endTimestamp"] == nextHourTimestamp:
|
||||||
|
isConnectionEnding = True
|
||||||
|
|
||||||
|
if not isConnectionEnding:
|
||||||
|
# There isn't a show that starts at the next hour, so we're returning the current connection.
|
||||||
|
return getCurrentShowConnection()
|
||||||
|
else:
|
||||||
|
# The show is ending, return no next show.
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def cleanOldConnections() -> None:
|
||||||
|
global connections
|
||||||
|
for i in range(len(connections)):
|
||||||
|
if connections[i]["endTimestamp"] < datetime.datetime.now().timestamp():
|
||||||
|
connections.pop(i)
|
||||||
|
|
||||||
|
|
||||||
|
def stateDecider() -> Dict[str, Any]:
|
||||||
|
currentConnection = getCurrentShowConnection()
|
||||||
|
nextConnection = getNextHourConnection()
|
||||||
|
print("currentConnection:", currentConnection)
|
||||||
|
print("nextConnection:", nextConnection)
|
||||||
|
willRunAutoNews = True
|
||||||
|
switchAudioAtMin = 2
|
||||||
|
newSelSource = None
|
||||||
|
newWSSource = None
|
||||||
|
if currentConnection != nextConnection:
|
||||||
|
print("Will be transitioning")
|
||||||
|
# The show is transitioning this hour.
|
||||||
|
if currentConnection:
|
||||||
|
print("There's a current connection.")
|
||||||
|
# Current show wants to end their show at 2 mins past
|
||||||
|
if currentConnection["autoNewsEnd"] == False:
|
||||||
|
print("This show doesn't want to end with news.")
|
||||||
|
willRunAutoNews = False
|
||||||
|
switchAudioAtMin = 2 # no real change here
|
||||||
|
if nextConnection:
|
||||||
|
print("There's a next connection.")
|
||||||
|
# next show wants to begin at 0 mins past hour.
|
||||||
|
if nextConnection["autoNewsBeginning"] == False:
|
||||||
|
print("The next connection doesn't want news at start.")
|
||||||
|
willRunAutoNews = False
|
||||||
|
switchAudioAtMin = 0
|
||||||
|
|
||||||
|
newSelSource = nextConnection["sourceid"]
|
||||||
|
newWSSource = nextConnection["wsid"] # None if show is not a WS.
|
||||||
|
else:
|
||||||
|
print("No next show, going back to jukebox.")
|
||||||
|
# There isn't a next show, go back to sustainer
|
||||||
|
newSelSource = SOURCE_JUKEBOX
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Show/sustainer is continuing for another hour.
|
||||||
|
print("Show / Sustainer is continuing this hour.")
|
||||||
|
if currentConnection:
|
||||||
|
print("We're currently doing a show, so check if they want middle news.")
|
||||||
|
willRunAutoNews = currentConnection["autoNewsMiddle"]
|
||||||
|
newSelSource = currentConnection["sourceid"]
|
||||||
|
newWSSource = currentConnection["wsid"]
|
||||||
|
elif SUSTAINER_AUTONEWS:
|
||||||
|
print("There's no show on currently, so we're going to AutoNEWS on sustainer")
|
||||||
|
# Jukebox -> NEWS -> Jukebox
|
||||||
|
newSelSource = SOURCE_JUKEBOX
|
||||||
|
else:
|
||||||
|
print("There's no show on currently, but AutoNews on sustainer is disabled, so don't do news")
|
||||||
|
# Jukebox -> Jukebox
|
||||||
|
newSelSource = SOURCE_JUKEBOX
|
||||||
|
switchAudioAtMin = 0
|
||||||
|
willRunAutoNews = False
|
||||||
|
|
||||||
|
nextState = {
|
||||||
|
"autoNews": willRunAutoNews,
|
||||||
|
"switchAudioAtMin": switchAudioAtMin,
|
||||||
|
"selSource": newSelSource,
|
||||||
|
"wsSource": newWSSource
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextState
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/status', methods=['GET'])
|
||||||
|
def get_status() -> Any:
|
||||||
|
print(getNextHourTimestamp())
|
||||||
|
global connections
|
||||||
|
cleanOldConnections()
|
||||||
|
return genPayload(
|
||||||
|
{
|
||||||
|
"connections": connections,
|
||||||
|
"wsSessions": wsSessions
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/nextTransition', methods=['GET'])
|
||||||
|
def get_next_transition() -> Any:
|
||||||
|
cleanOldConnections()
|
||||||
|
return genPayload(stateDecider())
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/cancelTimeslot', methods=['POST'])
|
||||||
|
def post_cancelCheck() -> Any:
|
||||||
|
global connections
|
||||||
|
content = request.json
|
||||||
|
if not content:
|
||||||
|
return genFail("No parameters provided.")
|
||||||
|
if not isinstance(content["connid"], int):
|
||||||
|
return genFail("Request missing valid connid.")
|
||||||
|
|
||||||
|
# We're gonna cancel their show.
|
||||||
|
currentShow = getCurrentShowConnection()
|
||||||
|
if currentShow and currentShow["connid"] == content["connid"]:
|
||||||
|
# this show is (at least supposed to be) live now.
|
||||||
|
# kill their show
|
||||||
|
do_ws_srv_telnet("NUL")
|
||||||
|
switch_proc = subprocess.Popen(["sel", str(SOURCE_JUKEBOX)])
|
||||||
|
pass
|
||||||
|
for i in range(len(connections)):
|
||||||
|
if connections[i]["connid"] == content["connid"]:
|
||||||
|
connections.pop(i)
|
||||||
|
return genPayload("Connection cancelled.")
|
||||||
|
return genFail("Connection not found.")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/registerTimeslot', methods=['POST'])
|
||||||
|
def post_registerCheck() -> Any:
|
||||||
|
global connections
|
||||||
|
global lastConnectionIDToRegister
|
||||||
|
|
||||||
|
content = request.json
|
||||||
|
if not content:
|
||||||
|
return genFail("No parameters provided.")
|
||||||
|
if not isinstance(content["timeslotid"], int):
|
||||||
|
return genFail("Request missing valid timeslotid.")
|
||||||
|
if not isinstance(content["memberid"], int):
|
||||||
|
return genFail("Request missing valid memberid.")
|
||||||
|
if not isinstance(content["sourceid"], int):
|
||||||
|
return genFail("Request missing valid source.")
|
||||||
|
if not content["sourceid"] in SOURCES:
|
||||||
|
return genFail("Request missing valid source.")
|
||||||
|
|
||||||
|
member = myradioApiRequest("user/" + str(content["memberid"]))
|
||||||
|
if not member:
|
||||||
|
return genFail("Could not get member.")
|
||||||
|
|
||||||
|
timeslot = myradioApiRequest("timeslot/" + str(content["timeslotid"]))
|
||||||
|
if not timeslot:
|
||||||
|
return genFail("Could not get tiemslot.")
|
||||||
|
|
||||||
|
found_credit = False
|
||||||
|
for credit in timeslot["credits"]:
|
||||||
|
if content["memberid"] == credit["memberid"]:
|
||||||
|
found_credit = True
|
||||||
|
break
|
||||||
|
if not found_credit:
|
||||||
|
return genFail("You are not authorised to broadcast for this timeslot.")
|
||||||
|
|
||||||
|
for conn in connections:
|
||||||
|
if content["timeslotid"] == conn["timeslotid"]:
|
||||||
|
# they've already registered, return the existing session
|
||||||
|
return genPayload(conn)
|
||||||
|
|
||||||
|
start_time = datetime.datetime.strptime(timeslot["start_time"], "%d/%m/%Y %H:%M")
|
||||||
|
|
||||||
|
duration = timeslot["duration"].split(":")
|
||||||
|
duration_time = datetime.timedelta(hours=int(duration[0]), minutes=int(duration[1]))
|
||||||
|
|
||||||
|
end_time = start_time + duration_time
|
||||||
|
|
||||||
|
now_time = datetime.datetime.now()
|
||||||
|
if start_time - now_time > datetime.timedelta(hours=1):
|
||||||
|
return genFail("This show too far away, please try again within an hour of starting your show.")
|
||||||
|
|
||||||
|
if start_time + duration_time < now_time:
|
||||||
|
return genFail("This show has already ended.")
|
||||||
|
|
||||||
|
random.seed(a=timeslot["timeslot_id"], version=2)
|
||||||
|
connection = {
|
||||||
|
"connid": random.randint(0, 100000000), # TODO: this is horrible. I'll sort this later.
|
||||||
|
"timeslotid": timeslot["timeslot_id"],
|
||||||
|
"startTimestamp": int(start_time.timestamp()),
|
||||||
|
"endTimestamp": int(end_time.timestamp()),
|
||||||
|
"sourceid": content["sourceid"],
|
||||||
|
'autoNewsBeginning': True,
|
||||||
|
'autoNewsMiddle': True,
|
||||||
|
'autoNewsEnd': True,
|
||||||
|
'wsid': None
|
||||||
|
}
|
||||||
|
connections.append(connection)
|
||||||
|
print(connections)
|
||||||
|
|
||||||
|
lastConnectionIDToRegister = connection["connid"]
|
||||||
|
|
||||||
|
return genPayload(connection)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/changeTimeslot', methods=['POST'])
|
||||||
|
def post_settingsCheck() -> Any:
|
||||||
|
global connections
|
||||||
|
content = request.json
|
||||||
|
if not content:
|
||||||
|
return genFail("No parameters provided.")
|
||||||
|
if not isinstance(content["connid"], int):
|
||||||
|
return genFail("Request missing valid connID.")
|
||||||
|
if not isinstance(content["beginning"], bool):
|
||||||
|
return genFail("Request missing valid beginning bool.")
|
||||||
|
if not isinstance(content["middle"], bool):
|
||||||
|
return genFail("Request missing valid middle bool.")
|
||||||
|
if not isinstance(content["end"], bool):
|
||||||
|
return genFail("Request missing valid end bool.")
|
||||||
|
if not isinstance(content["sourceid"], int):
|
||||||
|
return genFail("Request missing valid sourcid.")
|
||||||
|
|
||||||
|
for conn in connections:
|
||||||
|
if conn["connid"] == content["connid"]:
|
||||||
|
conn["autoNewsBeginning"] = content["beginning"]
|
||||||
|
conn["autoNewsMiddle"] = content["middle"]
|
||||||
|
conn["autoNewsEnd"] = content["end"]
|
||||||
|
conn["sourceid"] = content["sourceid"]
|
||||||
|
return genPayload(conn)
|
||||||
|
return genFail("No connection found.")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/updateWSSessions', methods=['POST'])
|
||||||
|
def post_wsSessions() -> Any:
|
||||||
|
global connections
|
||||||
|
global wsSessions
|
||||||
|
content = request.json
|
||||||
|
# if not content:
|
||||||
|
# return genFail("No parameters provided.")
|
||||||
|
oldSessions = wsSessions
|
||||||
|
|
||||||
|
wsSessions = content
|
||||||
|
print("New wsSessions:", wsSessions)
|
||||||
|
wsids_to_remove = []
|
||||||
|
wsids_to_add = []
|
||||||
|
for session in oldSessions:
|
||||||
|
if not oldSessions[session]["connection_id"] in wsSessions:
|
||||||
|
wsids_to_remove.append(oldSessions[session]["connection_id"])
|
||||||
|
|
||||||
|
print("wsSessions which have disappeared:", wsids_to_remove)
|
||||||
|
|
||||||
|
for session in wsSessions:
|
||||||
|
if not wsSessions[session]["connection_id"] in oldSessions:
|
||||||
|
wsids_to_add.append(wsSessions[session]["connection_id"])
|
||||||
|
|
||||||
|
print("wsSessions which have appeared:", wsids_to_add)
|
||||||
|
|
||||||
|
for conn in connections:
|
||||||
|
if conn["connid"] == lastConnectionIDToRegister:
|
||||||
|
if conn["wsid"] == None and len(wsids_to_add) == 1:
|
||||||
|
conn["wsid"] = wsids_to_add[0]
|
||||||
|
# time.sleep(5)
|
||||||
|
# TODO this doesn't exactly work right
|
||||||
|
do_ws_srv_telnet(conn["wsid"])
|
||||||
|
subprocess.Popen(['sel', '5'])
|
||||||
|
|
||||||
|
if conn["wsid"] in wsids_to_remove:
|
||||||
|
conn["wsid"] = None
|
||||||
|
# TODO Make this actually do a disconnect sequence if this is the current show.
|
||||||
|
# time.sleep(5)
|
||||||
|
subprocess.Popen(['sel', str(SOURCE_JUKEBOX)])
|
||||||
|
do_ws_srv_telnet("NUL")
|
||||||
|
return genPayload("Thx, K, bye.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True, host="0.0.0.0")
|
Loading…
Reference in a new issue