Replace pip with Poetry and upgrades for Python 3.10

This commit is contained in:
Marks Polakovs 2023-03-09 23:04:44 +00:00
parent 2dc3c8cd3c
commit 7d18f086e8
8 changed files with 1944 additions and 166 deletions

10
Jenkinsfile vendored
View file

@ -15,8 +15,7 @@ pipeline {
} }
stage('Python') { stage('Python') {
steps { steps {
sh '/usr/local/bin/python3.7 -m venv env' sh 'poetry install'
sh 'env/bin/pip install -r requirements.ci.txt'
} }
} }
} }
@ -31,12 +30,12 @@ pipeline {
} }
stage('MyPy (stateserver)') { stage('MyPy (stateserver)') {
steps { steps {
sh 'env/bin/mypy stateserver.py' sh 'poetry run mypy stateserver.py'
} }
} }
stage('MyPy (shittyserver)') { stage('MyPy (shittyserver)') {
steps { steps {
sh 'env/bin/mypy shittyserver.py' sh 'poetry run mypy shittyserver.py'
} }
} }
} }
@ -118,7 +117,8 @@ pipeline {
sshagent(credentials: ['ury']) { sshagent(credentials: ['ury']) {
sh 'scp -v -o StrictHostKeyChecking=no stateserver.py liquidsoap@dolby.ury:/opt/webstudioserver/stateserver.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 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 pyproject.toml liquidsoap@dolby.ury:/opt/webstudioserver/pyproject.toml'
sh 'scp -v -o StrictHostKeyChecking=no poetry.lock liquidsoap@dolby.ury:/opt/webstudioserver/poetry.lock'
} }
} }
} }

View file

@ -10,11 +10,11 @@ The clientside is written in TypeScript using React and Redux, the serverside is
Client: Client:
* Node.js and Yarn 1.x - Node.js and Yarn 1.x
Server: Server:
* Python 3.7-3.9 (note: Python 3.10 is not supported) - Python 3.7-3.9 (note: Python 3.10 is not supported)
### Installing ### Installing
@ -22,12 +22,10 @@ Clone the repo and run `yarn`.
You'll probably want to change the values in `.env` to reflect the MyRadio environment and/or where the server is running (e.g. if you're running the server locally, change `REACT_APP_WS_URL` to `ws://localhost:8079/stream`). You'll probably want to change the values in `.env` to reflect the MyRadio environment and/or where the server is running (e.g. if you're running the server locally, change `REACT_APP_WS_URL` to `ws://localhost:8079/stream`).
If you want to hack on the server, create a virtualenv and install Python packages: If you want to hack on the server, use [Poetry](https://python-poetry.org/docs/) create a virtualenv and install Python packages:
```sh ```sh
$ python3 -m venv venv $ poetry install
$ source venv/bin/activate
$ pip install -r requirements.txt
``` ```
### Versions ### Versions
@ -65,6 +63,7 @@ This is done via the BAPSicle project by updating the `/presenter` submodule, si
If you want to demo build a BAPS Presenter release, run `npm run build-baps` and the result will be in the `build` directory. If you want to demo build a BAPS Presenter release, run `npm run build-baps` and the result will be in the `build` directory.
## Screenshots ## Screenshots
![Mic Live With Main Screen](images/HomeWithMic.png?raw=true "Mic Live on Main Screen") ![Mic Live With Main Screen](images/HomeWithMic.png?raw=true "Mic Live on Main Screen")
![Home Page of webstudio](images/Home.png?raw=true "Home Page of WebStudio") ![Home Page of webstudio](images/Home.png?raw=true "Home Page of WebStudio")

1767
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

29
pyproject.toml Normal file
View file

@ -0,0 +1,29 @@
[tool.poetry]
name = "webstudio"
version = "1.6.0"
description = ""
authors = ["Marks Polakovs <marks.polakovs@ury.org.uk>", "Matthew Stratford <matthew.stratford@ury.org.uk>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.7"
aiohttp = "^3.8.4"
av = "^10.0.0"
jack-client = "^0.5.4"
websockets = "^10.4"
aiortc = "^1.4.0"
sentry-sdk = "^1.16.0"
requests = "^2.28.2"
flask = "^2.2.3"
flask-cors = "^3.0.10"
[tool.poetry.group.dev.dependencies]
black = "^23.1.0"
mypy = "^1.1.1"
types-jack-client = "^0.5.10.7"
types-requests = "^2.28.11.15"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View file

@ -1,21 +0,0 @@
aiohttp==3.7.4
async-timeout==3.0.1
attrs==19.3.0
certifi==2020.4.5.1
chardet==3.0.4
click==7.1.2
Flask==1.1.2
idna==2.9
itsdangerous==1.1.0
Jinja2==2.11.3
MarkupSafe==1.1.1
multidict==5.1.0
mypy==0.770
mypy-extensions==0.4.3
requests==2.26.0
typed-ast==1.4.1
typing-extensions==3.7.4.2
urllib3==1.26.5
websockets==9.1
Werkzeug==1.0.1
yarl==1.4.2

View file

@ -1,56 +0,0 @@
aiohttp==3.7.4
aioice==0.6.18
aiortc==0.9.27
appdirs==1.4.4
async-timeout==3.0.1
attrs==19.3.0
av==7.0.1
black==20.8b1
blinker==1.4
certifi==2020.4.5.1
cffi==1.14.0
chardet==3.0.4
click==7.1.2
crc32c==2.0
cryptography==3.3.2
expiringdict==1.2.1
Flask==1.1.2
Flask-Cors==3.0.9
gunicorn==20.0.4
idna==2.9
itsdangerous==1.1.0
JACK-Client==0.5.2
jedi==0.15.2
Jinja2==2.11.3
jsonpickle==1.3
MarkupSafe==1.1.1
multidict==5.1.0
mypy==0.770
mypy-extensions==0.4.3
netifaces==0.10.9
parso==0.6.2
pathspec==0.8.1
pluggy==0.13.1
pycparser==2.20
pyee==7.0.1
PyJWT==1.7.1
pylibsrtp==0.6.6
pyls==0.1.6
pyls-mypy==0.1.8
python-dateutil==2.8.1
python-jsonrpc-server==0.3.4
python-language-server==0.31.9
pytz==2019.3
regex==2020.11.13
requests==2.26.0
sentry-sdk==1.0.0
six==1.14.0
toml==0.10.2
twilio==6.38.1
typed-ast==1.4.1
typing-extensions==3.7.4.2
ujson==1.35
urllib3==1.26.5
websockets==9.1
Werkzeug==1.0.1
yarl==1.4.2

View file

@ -11,20 +11,18 @@ from typing import Optional, Any, Type, Dict, List
import aiohttp import aiohttp
import av # type: ignore import av # type: ignore
import jack as Jack # type: ignore import jack as Jack
import websockets from jack import OwnPort
import websockets.exceptions, websockets.server, websockets.connection
from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription # type: ignore from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription # type: ignore
from aiortc.mediastreams import MediaStreamError # type: ignore from aiortc.mediastreams import MediaStreamError # type: ignore
import sentry_sdk # type: ignore import sentry_sdk
config = configparser.RawConfigParser() config = configparser.RawConfigParser()
config.read("serverconfig.ini") config.read("serverconfig.ini")
if config.get("sentry", "enable") == "True": if config.get("sentry", "enable") == "True":
sentry_sdk.init( sentry_sdk.init(config.get("sentry", "dsn"), traces_sample_rate=1.0)
config.get("sentry", "dsn"),
traces_sample_rate=1.0
)
file_contents_ex = re.compile(r"^ws=\d$") file_contents_ex = re.compile(r"^ws=\d$")
@ -53,7 +51,10 @@ def get_turn_credentials() -> TurnCredentials:
provider = config.get("shittyserver", "turn_provider") provider = config.get("shittyserver", "turn_provider")
if provider == "twilio": if provider == "twilio":
from twilio.rest import Client # type: ignore from twilio.rest import Client # type: ignore
client = Client(config.get("twilio", "account_sid"), config.get("twilio", "auth_token"))
client = Client(
config.get("twilio", "account_sid"), config.get("twilio", "auth_token")
)
token = client.tokens.create() token = client.tokens.create()
# Twilio's typedef is wrong, reee # Twilio's typedef is wrong, reee
@ -65,19 +66,19 @@ def get_turn_credentials() -> TurnCredentials:
raise Exception("unknown provider " + provider) raise Exception("unknown provider " + provider)
@Jack.set_error_function # type: ignore @Jack.set_error_function
def error(msg: str) -> None: def error(msg: str) -> None:
print("Error:", msg) print("Error:", msg)
@Jack.set_info_function # type: ignore @Jack.set_info_function
def info(msg: str) -> None: def info(msg: str) -> None:
print("Info:", msg) print("Info:", msg)
jack = Jack.Client("webstudio") jack = Jack.Client("webstudio")
out1 = jack.outports.register("out_0") out1: OwnPort = jack.outports.register("out_0") # type: ignore
out2 = jack.outports.register("out_1") out2: OwnPort = jack.outports.register("out_1") # type: ignore
transfer_buffer1: Any = None transfer_buffer1: Any = None
transfer_buffer2: Any = None transfer_buffer2: Any = None
@ -92,19 +93,19 @@ def init_buffers() -> None:
init_buffers() init_buffers()
@jack.set_process_callback # type: ignore @jack.set_process_callback
def process(frames: int) -> None: def process(frames: int) -> None:
buf1 = out1.get_buffer() buf1 = out1.get_buffer()
if transfer_buffer1.read_space == 0: if transfer_buffer1.read_space == 0:
for i in range(len(buf1)): for i in range(len(buf1)):
buf1[i] = b'\x00' buf1[i] = b"\x00" # type: ignore
else: else:
piece1 = transfer_buffer1.read(len(buf1)) piece1 = transfer_buffer1.read(len(buf1))
buf1[: len(piece1)] = piece1 buf1[: len(piece1)] = piece1
buf2 = out2.get_buffer() buf2 = out2.get_buffer()
if transfer_buffer2.read_space == 0: if transfer_buffer2.read_space == 0:
for i in range(len(buf2)): for i in range(len(buf2)):
buf2[i] = b'\x00' buf2[i] = b"\x00" # type: ignore
else: else:
piece2 = transfer_buffer2.read(len(buf2)) piece2 = transfer_buffer2.read(len(buf2))
buf2[: len(piece2)] = piece2 buf2[: len(piece2)] = piece2
@ -119,7 +120,9 @@ 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("shittyserver", "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)
@ -128,7 +131,7 @@ class NotReadyException(BaseException):
class Session(object): class Session(object):
websocket: Optional[websockets.WebSocketServerProtocol] websocket: Optional[websockets.server.WebSocketServerProtocol]
connection_state: Optional[str] connection_state: Optional[str]
pc: Optional[Any] pc: Optional[Any]
connection_id: str connection_id: str
@ -153,7 +156,7 @@ class Session(object):
def to_dict(self) -> Dict[str, str]: def to_dict(self) -> Dict[str, str]:
return { return {
"connection_id": self.connection_id, "connection_id": self.connection_id,
"connected_at": self.connected_at.strftime("%Y-%m-%dT%H:%M:%S%z") "connected_at": self.connected_at.strftime("%Y-%m-%dT%H:%M:%S%z"),
} }
async def activate(self) -> None: async def activate(self) -> None:
@ -169,7 +172,9 @@ class Session(object):
try: try:
await self.websocket.send(json.dumps({"kind": "DEACTIVATED"})) await self.websocket.send(json.dumps({"kind": "DEACTIVATED"}))
except websockets.exceptions.ConnectionClosed: except websockets.exceptions.ConnectionClosed:
print(self.connection_id, "not sending DEACTIVATED as it's already closed") print(
self.connection_id, "not sending DEACTIVATED as it's already closed"
)
pass pass
async def end(self) -> None: async def end(self) -> None:
@ -188,14 +193,16 @@ class Session(object):
await self.pc.close() await self.pc.close()
if ( if (
self.websocket is not None self.websocket is not None
and self.websocket.state == websockets.protocol.State.OPEN and self.websocket.state == websockets.connection.State.OPEN
): ):
try: try:
await self.websocket.send(json.dumps({"kind": "DIED"})) await self.websocket.send(json.dumps({"kind": "DIED"}))
await self.websocket.close(1008) await self.websocket.close(1008)
except websockets.exceptions.ConnectionClosed: except websockets.exceptions.ConnectionClosed:
print(self.connection_id, "socket already closed, no died message") print(
self.connection_id, "socket already closed, no died message"
)
if self.connection_id in active_sessions: if self.connection_id in active_sessions:
print(self.connection_id, "removing from active_sessions") print(self.connection_id, "removing from active_sessions")
@ -253,7 +260,10 @@ class Session(object):
@track.on("ended") # type: ignore @track.on("ended") # type: ignore
async def on_ended() -> None: async def on_ended() -> None:
print(self.connection_id, "Ending due to {} track end".format(track.kind)) print(
self.connection_id,
"Ending due to {} track end".format(track.kind),
)
await self.end() await self.end()
write_ob_status(True) write_ob_status(True)
@ -314,7 +324,9 @@ class Session(object):
), ),
) )
async def connect(self, websocket: websockets.WebSocketServerProtocol) -> None: async def connect(
self, websocket: websockets.server.WebSocketServerProtocol
) -> None:
global active_sessions global active_sessions
active_sessions[self.connection_id] = self active_sessions[self.connection_id] = self
@ -326,7 +338,13 @@ class Session(object):
print(self.connection_id, "Obtained ICE") print(self.connection_id, "Obtained ICE")
sentry_sdk.set_context("session", {"session_id": self.connection_id}) sentry_sdk.set_context("session", {"session_id": self.connection_id})
await websocket.send( await websocket.send(
json.dumps({"kind": "HELLO", "connectionId": self.connection_id, "iceServers": ice_config}) json.dumps(
{
"kind": "HELLO",
"connectionId": self.connection_id,
"iceServers": ice_config,
}
)
) )
try: try:
@ -350,7 +368,9 @@ class Session(object):
await self.end() await self.end()
async def serve(websocket: websockets.WebSocketServerProtocol, path: str) -> None: async def serve(
websocket: websockets.server.WebSocketServerProtocol, path: str
) -> None:
if path == "/stream": if path == "/stream":
session = Session() session = Session()
await session.connect(websocket) await session.connect(websocket)
@ -358,15 +378,19 @@ async def serve(websocket: websockets.WebSocketServerProtocol, path: str) -> Non
pass pass
start_server = websockets.serve( start_server = websockets.server.serve(
serve, host=None, port=int(config.get("shittyserver", "websocket_port")) serve, host=None, port=int(config.get("shittyserver", "websocket_port"))
) )
print("Shittyserver WS starting on port {}.".format(config.get("shittyserver", "websocket_port"))) print(
"Shittyserver WS starting on port {}.".format(
config.get("shittyserver", "websocket_port")
)
)
async def telnet_server( async def telnet_server(
reader: asyncio.StreamReader, writer: asyncio.StreamWriter reader: asyncio.StreamReader, writer: asyncio.StreamWriter
) -> None: ) -> None:
global active_sessions, live_session global active_sessions, live_session
while True: while True:
@ -387,15 +411,15 @@ async def telnet_server(
result[sid] = sess.to_dict() result[sid] = sess.to_dict()
writer.write( writer.write(
( (
json.dumps( json.dumps(
{ {
"live": live_session.to_dict() "live": live_session.to_dict()
if live_session is not None if live_session is not None
else None, else None,
"active": result, "active": result,
} }
) )
+ "\r\n" + "\r\n"
).encode("utf-8") ).encode("utf-8")
) )
@ -444,7 +468,11 @@ async def run_telnet_server() -> None:
jack.activate() jack.activate()
print("Shittyserver TELNET starting on port {}".format(config.get("shittyserver", "telnet_port"))) 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(

View file

@ -6,7 +6,7 @@ import subprocess
from typing import List, Any, Dict, Optional from typing import List, Any, Dict, Optional
from flask import Flask, jsonify, request from flask import Flask, jsonify, request
from flask_cors import CORS # type: ignore from flask_cors import CORS # type: ignore
import requests import requests
import datetime import datetime
import random import random
@ -25,7 +25,11 @@ SUSTAINER_AUTONEWS = config.get("stateserver", "sustainer_autonews") == "True"
def do_ws_srv_telnet(source: str) -> None: def do_ws_srv_telnet(source: str) -> None:
HOST = "localhost" HOST = "localhost"
print("telnet {} {} SEL {}".format(HOST, config.get("shittyserver", "telnet_port"), source)) print(
"telnet {} {} SEL {}".format(
HOST, config.get("shittyserver", "telnet_port"), source
)
)
tn = Telnet(HOST, int(config.get("shittyserver", "telnet_port"))) tn = Telnet(HOST, int(config.get("shittyserver", "telnet_port")))
tn.write(b"SEL " + str.encode(source) + b"\n") tn.write(b"SEL " + str.encode(source) + b"\n")
try: try:
@ -45,7 +49,7 @@ def genPayload(payload: Any) -> Any:
def myradioApiRequest(url: str) -> Any: def myradioApiRequest(url: str) -> Any:
res = requests.get('https://ury.org.uk/api/v2/' + url + '?api_key=' + api_key) res = requests.get("https://ury.org.uk/api/v2/" + url + "?api_key=" + api_key)
if res.ok: if res.ok:
return res.json()["payload"] return res.json()["payload"]
else: else:
@ -85,7 +89,8 @@ wsSessions: Dict[str, Dict[str, str]] = {}
def getCurrentShowConnection() -> Optional[Connection]: def getCurrentShowConnection() -> Optional[Connection]:
for connection in connections: for connection in connections:
if (connection["startTimestamp"] <= datetime.datetime.now().timestamp()) and ( if (connection["startTimestamp"] <= datetime.datetime.now().timestamp()) and (
connection["endTimestamp"] >= getNextHourTimestamp()): connection["endTimestamp"] >= getNextHourTimestamp()
):
return connection return connection
return None return None
@ -109,8 +114,8 @@ def getNextHourConnection() -> Optional[Connection]:
def cleanOldConnections() -> None: def cleanOldConnections() -> None:
global connections global connections
#Go backwards round the loop so that pop's don't interfere with the index. # Go backwards round the loop so that pop's don't interfere with the index.
for i in range(len(connections)-1,-1,-1): for i in range(len(connections) - 1, -1, -1):
if connections[i]["endTimestamp"] < datetime.datetime.now().timestamp(): if connections[i]["endTimestamp"] < datetime.datetime.now().timestamp():
connections.pop(i) connections.pop(i)
@ -159,11 +164,15 @@ def stateDecider() -> Dict[str, Any]:
newSelSource = currentConnection["sourceid"] newSelSource = currentConnection["sourceid"]
newWSSource = currentConnection["wsid"] newWSSource = currentConnection["wsid"]
elif SUSTAINER_AUTONEWS: elif SUSTAINER_AUTONEWS:
print("There's no show on currently, so we're going to AutoNEWS on sustainer") print(
"There's no show on currently, so we're going to AutoNEWS on sustainer"
)
# Jukebox -> NEWS -> Jukebox # Jukebox -> NEWS -> Jukebox
newSelSource = SOURCE_JUKEBOX newSelSource = SOURCE_JUKEBOX
else: else:
print("There's no show on currently, but AutoNews on sustainer is disabled, so don't do news") print(
"There's no show on currently, but AutoNews on sustainer is disabled, so don't do news"
)
# Jukebox -> Jukebox # Jukebox -> Jukebox
newSelSource = SOURCE_JUKEBOX newSelSource = SOURCE_JUKEBOX
switchAudioAtMin = 0 switchAudioAtMin = 0
@ -173,32 +182,27 @@ def stateDecider() -> Dict[str, Any]:
"autoNews": willRunAutoNews, "autoNews": willRunAutoNews,
"switchAudioAtMin": switchAudioAtMin, "switchAudioAtMin": switchAudioAtMin,
"selSource": newSelSource, "selSource": newSelSource,
"wsSource": newWSSource "wsSource": newWSSource,
} }
return nextState return nextState
@app.route('/api/v1/status', methods=['GET']) @app.route("/api/v1/status", methods=["GET"])
def get_status() -> Any: def get_status() -> Any:
print(getNextHourTimestamp()) print(getNextHourTimestamp())
global connections global connections
cleanOldConnections() cleanOldConnections()
return genPayload( return genPayload({"connections": connections, "wsSessions": wsSessions})
{
"connections": connections,
"wsSessions": wsSessions
}
)
@app.route('/api/v1/nextTransition', methods=['GET']) @app.route("/api/v1/nextTransition", methods=["GET"])
def get_next_transition() -> Any: def get_next_transition() -> Any:
cleanOldConnections() cleanOldConnections()
return genPayload(stateDecider()) return genPayload(stateDecider())
@app.route('/api/v1/cancelTimeslot', methods=['POST']) @app.route("/api/v1/cancelTimeslot", methods=["POST"])
def post_cancelCheck() -> Any: def post_cancelCheck() -> Any:
global connections global connections
content = request.json content = request.json
@ -215,7 +219,13 @@ def post_cancelCheck() -> Any:
# but don't kill it during the news, or after the end time, to avoid unexpected jukeboxing # but don't kill it during the news, or after the end time, to avoid unexpected jukeboxing
now = datetime.datetime.now().timestamp() now = datetime.datetime.now().timestamp()
if now < (currentShow["endTimestamp"] - 15): if now < (currentShow["endTimestamp"] - 15):
print("Jukeboxing due to {}'s ({}, {}) cancellation".format(currentShow["connid"], currentShow["timeslotid"], currentShow["wsid"])) print(
"Jukeboxing due to {}'s ({}, {}) cancellation".format(
currentShow["connid"],
currentShow["timeslotid"],
currentShow["wsid"],
)
)
do_ws_srv_telnet("NUL") do_ws_srv_telnet("NUL")
subprocess.Popen(["sel", str(SOURCE_JUKEBOX)]) subprocess.Popen(["sel", str(SOURCE_JUKEBOX)])
@ -227,7 +237,7 @@ def post_cancelCheck() -> Any:
return genFail("Connection not found.") return genFail("Connection not found.")
@app.route('/api/v1/registerTimeslot', methods=['POST']) @app.route("/api/v1/registerTimeslot", methods=["POST"])
def post_registerCheck() -> Any: def post_registerCheck() -> Any:
global connections global connections
@ -274,45 +284,62 @@ def post_registerCheck() -> Any:
for conn in connections: for conn in connections:
if content["timeslotid"] == conn["timeslotid"]: if content["timeslotid"] == conn["timeslotid"]:
# they've already registered, return the existing session # they've already registered, return the existing session
print("found existing connection {} for {}".format(conn["connid"], conn["timeslotid"])) print(
"found existing connection {} for {}".format(
conn["connid"], conn["timeslotid"]
)
)
connection = conn connection = conn
# make sure we update their wsID # make sure we update their wsID
if "wsid" in content: if "wsid" in content:
connection["wsid"] = content["wsid"] connection["wsid"] = content["wsid"]
new_connection = False new_connection = False
if connection is None: if connection is None:
new_connection = True new_connection = True
if start_time - now_time > datetime.timedelta(hours=1): 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.") return genFail(
"This show too far away, please try again within an hour of starting your show."
)
if start_time + duration_time < now_time: if start_time + duration_time < now_time:
return genFail("This show has already ended.") return genFail("This show has already ended.")
if start_time - datetime.timedelta(minutes=1) < now_time < start_time + datetime.timedelta(minutes=2): if (
return genFail("You registered too late. Please re-register after the news.") start_time - datetime.timedelta(minutes=1)
< now_time
< start_time + datetime.timedelta(minutes=2)
):
return genFail(
"You registered too late. Please re-register after the news."
)
random.seed(a=timeslot["timeslot_id"], version=2) random.seed(a=timeslot["timeslot_id"], version=2)
connection = { connection = {
"connid": random.randint(0, 100000000), # TODO: this is horrible. I'll sort this later. "connid": random.randint(
0, 100000000
), # TODO: this is horrible. I'll sort this later.
"timeslotid": timeslot["timeslot_id"], "timeslotid": timeslot["timeslot_id"],
"startTimestamp": int(start_time.timestamp()), "startTimestamp": int(start_time.timestamp()),
"endTimestamp": int(end_time.timestamp()), "endTimestamp": int(end_time.timestamp()),
"sourceid": content["sourceid"], "sourceid": content["sourceid"],
'autoNewsBeginning': True, "autoNewsBeginning": True,
'autoNewsMiddle': True, "autoNewsMiddle": True,
'autoNewsEnd': True, "autoNewsEnd": True,
'wsid': content["wsid"] "wsid": content["wsid"],
} }
if start_time + datetime.timedelta(minutes=2) < now_time: if start_time + datetime.timedelta(minutes=2) < now_time:
if connection["wsid"] is not None: if connection["wsid"] is not None:
# they're late, bring them live now # they're late, bring them live now
print("({}, {}) late, bringing on air now".format(connection["connid"], connection["wsid"])) print(
"({}, {}) late, bringing on air now".format(
connection["connid"], connection["wsid"]
)
)
do_ws_srv_telnet(connection["wsid"]) do_ws_srv_telnet(connection["wsid"])
subprocess.Popen(['sel', '5']) subprocess.Popen(["sel", "5"])
assert connection is not None assert connection is not None
if new_connection: if new_connection:
@ -322,7 +349,7 @@ def post_registerCheck() -> Any:
return genPayload(connection) return genPayload(connection)
@app.route('/api/v1/changeTimeslot', methods=['POST']) @app.route("/api/v1/changeTimeslot", methods=["POST"])
def post_settingsCheck() -> Any: def post_settingsCheck() -> Any:
global connections global connections
content = request.json content = request.json
@ -349,11 +376,12 @@ def post_settingsCheck() -> Any:
return genFail("No connection found.") return genFail("No connection found.")
@app.route('/api/v1/updateWSSessions', methods=['POST']) @app.route("/api/v1/updateWSSessions", methods=["POST"])
def post_wsSessions() -> Any: def post_wsSessions() -> Any:
global connections global connections
global wsSessions global wsSessions
content = request.json content = request.json
assert content is not None
# if not content: # if not content:
# return genFail("No parameters provided.") # return genFail("No parameters provided.")
oldSessions = wsSessions oldSessions = wsSessions
@ -378,9 +406,13 @@ def post_wsSessions() -> Any:
if conn["wsid"] in wsids_to_add: if conn["wsid"] in wsids_to_add:
if conn["startTimestamp"] + 120 < datetime.datetime.now().timestamp(): if conn["startTimestamp"] + 120 < datetime.datetime.now().timestamp():
# they're late, bring them on air now # they're late, bring them on air now
print("({}, {}) late, bringing on air now".format(conn["connid"], conn["wsid"])) print(
"({}, {}) late, bringing on air now".format(
conn["connid"], conn["wsid"]
)
)
do_ws_srv_telnet(conn["wsid"]) do_ws_srv_telnet(conn["wsid"])
subprocess.Popen(['sel', '5']) subprocess.Popen(["sel", "5"])
if conn["wsid"] in wsids_to_remove: if conn["wsid"] in wsids_to_remove:
print("({}, {}) gone".format(conn["connid"], conn["wsid"])) print("({}, {}) gone".format(conn["connid"], conn["wsid"]))
@ -394,10 +426,10 @@ def post_wsSessions() -> Any:
now = datetime.datetime.now().timestamp() now = datetime.datetime.now().timestamp()
if now < (currentShow["endTimestamp"] - 15): if now < (currentShow["endTimestamp"] - 15):
print("jukeboxing due to their disappearance...") print("jukeboxing due to their disappearance...")
subprocess.Popen(['sel', str(SOURCE_JUKEBOX)]) subprocess.Popen(["sel", str(SOURCE_JUKEBOX)])
do_ws_srv_telnet("NUL") do_ws_srv_telnet("NUL")
return genPayload("Thx, K, bye.") return genPayload("Thx, K, bye.")
if __name__ == '__main__': if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0") app.run(debug=True, host="0.0.0.0")