Merge pull request #284 from UniversityRadioYork/marks/upgrades
Replace pip with Poetry and upgrades for Python 3.10
This commit is contained in:
commit
0d953961ef
8 changed files with 1944 additions and 166 deletions
10
Jenkinsfile
vendored
10
Jenkinsfile
vendored
|
@ -15,8 +15,7 @@ pipeline {
|
|||
}
|
||||
stage('Python') {
|
||||
steps {
|
||||
sh '/usr/local/bin/python3.7 -m venv env'
|
||||
sh 'env/bin/pip install -r requirements.ci.txt'
|
||||
sh 'poetry install'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,12 +30,12 @@ pipeline {
|
|||
}
|
||||
stage('MyPy (stateserver)') {
|
||||
steps {
|
||||
sh 'env/bin/mypy stateserver.py'
|
||||
sh 'poetry run mypy stateserver.py'
|
||||
}
|
||||
}
|
||||
stage('MyPy (shittyserver)') {
|
||||
steps {
|
||||
sh 'env/bin/mypy shittyserver.py'
|
||||
sh 'poetry run mypy shittyserver.py'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +117,8 @@ pipeline {
|
|||
sshagent(credentials: ['ury']) {
|
||||
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 pyproject.toml liquidsoap@dolby.ury:/opt/webstudioserver/pyproject.toml'
|
||||
sh 'scp -v -o StrictHostKeyChecking=no poetry.lock liquidsoap@dolby.ury:/opt/webstudioserver/poetry.lock'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
README.md
11
README.md
|
@ -10,11 +10,11 @@ The clientside is written in TypeScript using React and Redux, the serverside is
|
|||
|
||||
Client:
|
||||
|
||||
* Node.js and Yarn 1.x
|
||||
- Node.js and Yarn 1.x
|
||||
|
||||
Server:
|
||||
|
||||
* Python 3.7-3.9 (note: Python 3.10 is not supported)
|
||||
- Python 3.7-3.10
|
||||
|
||||
### 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`).
|
||||
|
||||
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
|
||||
$ python3 -m venv venv
|
||||
$ source venv/bin/activate
|
||||
$ pip install -r requirements.txt
|
||||
$ poetry install
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
## Screenshots
|
||||
|
||||
![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")
|
||||
|
|
1767
poetry.lock
generated
Normal file
1767
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
29
pyproject.toml
Normal file
29
pyproject.toml
Normal 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"
|
|
@ -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
|
|
@ -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
|
106
shittyserver.py
106
shittyserver.py
|
@ -11,20 +11,18 @@ from typing import Optional, Any, Type, Dict, List
|
|||
|
||||
import aiohttp
|
||||
import av # type: ignore
|
||||
import jack as Jack # type: ignore
|
||||
import websockets
|
||||
import jack as Jack
|
||||
from jack import OwnPort
|
||||
import websockets.exceptions, websockets.server, websockets.connection
|
||||
from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription # type: ignore
|
||||
from aiortc.mediastreams import MediaStreamError # type: ignore
|
||||
import sentry_sdk # type: ignore
|
||||
import sentry_sdk
|
||||
|
||||
config = configparser.RawConfigParser()
|
||||
config.read("serverconfig.ini")
|
||||
|
||||
if config.get("sentry", "enable") == "True":
|
||||
sentry_sdk.init(
|
||||
config.get("sentry", "dsn"),
|
||||
traces_sample_rate=1.0
|
||||
)
|
||||
sentry_sdk.init(config.get("sentry", "dsn"), traces_sample_rate=1.0)
|
||||
|
||||
file_contents_ex = re.compile(r"^ws=\d$")
|
||||
|
||||
|
@ -53,7 +51,10 @@ def get_turn_credentials() -> TurnCredentials:
|
|||
provider = config.get("shittyserver", "turn_provider")
|
||||
if provider == "twilio":
|
||||
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()
|
||||
# Twilio's typedef is wrong, reee
|
||||
|
@ -65,19 +66,19 @@ def get_turn_credentials() -> TurnCredentials:
|
|||
raise Exception("unknown provider " + provider)
|
||||
|
||||
|
||||
@Jack.set_error_function # type: ignore
|
||||
@Jack.set_error_function
|
||||
def error(msg: str) -> None:
|
||||
print("Error:", msg)
|
||||
|
||||
|
||||
@Jack.set_info_function # type: ignore
|
||||
@Jack.set_info_function
|
||||
def info(msg: str) -> None:
|
||||
print("Info:", msg)
|
||||
|
||||
|
||||
jack = Jack.Client("webstudio")
|
||||
out1 = jack.outports.register("out_0")
|
||||
out2 = jack.outports.register("out_1")
|
||||
out1: OwnPort = jack.outports.register("out_0") # type: ignore
|
||||
out2: OwnPort = jack.outports.register("out_1") # type: ignore
|
||||
|
||||
transfer_buffer1: Any = None
|
||||
transfer_buffer2: Any = None
|
||||
|
@ -92,19 +93,19 @@ def init_buffers() -> None:
|
|||
init_buffers()
|
||||
|
||||
|
||||
@jack.set_process_callback # type: ignore
|
||||
@jack.set_process_callback
|
||||
def process(frames: int) -> None:
|
||||
buf1 = out1.get_buffer()
|
||||
if transfer_buffer1.read_space == 0:
|
||||
for i in range(len(buf1)):
|
||||
buf1[i] = b'\x00'
|
||||
buf1[i] = b"\x00" # type: ignore
|
||||
else:
|
||||
piece1 = transfer_buffer1.read(len(buf1))
|
||||
buf1[: len(piece1)] = piece1
|
||||
buf2 = out2.get_buffer()
|
||||
if transfer_buffer2.read_space == 0:
|
||||
for i in range(len(buf2)):
|
||||
buf2[i] = b'\x00'
|
||||
buf2[i] = b"\x00" # type: ignore
|
||||
else:
|
||||
piece2 = transfer_buffer2.read(len(buf2))
|
||||
buf2[: len(piece2)] = piece2
|
||||
|
@ -119,7 +120,9 @@ async def notify_mattserver_about_sessions() -> None:
|
|||
data: Dict[str, Dict[str, str]] = {}
|
||||
for sid, sess in active_sessions.items():
|
||||
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)
|
||||
|
||||
|
||||
|
@ -128,7 +131,7 @@ class NotReadyException(BaseException):
|
|||
|
||||
|
||||
class Session(object):
|
||||
websocket: Optional[websockets.WebSocketServerProtocol]
|
||||
websocket: Optional[websockets.server.WebSocketServerProtocol]
|
||||
connection_state: Optional[str]
|
||||
pc: Optional[Any]
|
||||
connection_id: str
|
||||
|
@ -153,7 +156,7 @@ class Session(object):
|
|||
def to_dict(self) -> Dict[str, str]:
|
||||
return {
|
||||
"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:
|
||||
|
@ -169,7 +172,9 @@ class Session(object):
|
|||
try:
|
||||
await self.websocket.send(json.dumps({"kind": "DEACTIVATED"}))
|
||||
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
|
||||
|
||||
async def end(self) -> None:
|
||||
|
@ -188,14 +193,16 @@ class Session(object):
|
|||
await self.pc.close()
|
||||
|
||||
if (
|
||||
self.websocket is not None
|
||||
and self.websocket.state == websockets.protocol.State.OPEN
|
||||
self.websocket is not None
|
||||
and self.websocket.state == websockets.connection.State.OPEN
|
||||
):
|
||||
try:
|
||||
await self.websocket.send(json.dumps({"kind": "DIED"}))
|
||||
await self.websocket.close(1008)
|
||||
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:
|
||||
print(self.connection_id, "removing from active_sessions")
|
||||
|
@ -253,7 +260,10 @@ class Session(object):
|
|||
|
||||
@track.on("ended") # type: ignore
|
||||
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()
|
||||
|
||||
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
|
||||
|
||||
active_sessions[self.connection_id] = self
|
||||
|
@ -326,7 +338,13 @@ class Session(object):
|
|||
print(self.connection_id, "Obtained ICE")
|
||||
sentry_sdk.set_context("session", {"session_id": self.connection_id})
|
||||
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:
|
||||
|
@ -350,7 +368,9 @@ class Session(object):
|
|||
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":
|
||||
session = Session()
|
||||
await session.connect(websocket)
|
||||
|
@ -358,15 +378,19 @@ async def serve(websocket: websockets.WebSocketServerProtocol, path: str) -> Non
|
|||
pass
|
||||
|
||||
|
||||
start_server = websockets.serve(
|
||||
start_server = websockets.server.serve(
|
||||
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(
|
||||
reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
||||
reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
||||
) -> None:
|
||||
global active_sessions, live_session
|
||||
while True:
|
||||
|
@ -387,15 +411,15 @@ async def telnet_server(
|
|||
result[sid] = sess.to_dict()
|
||||
writer.write(
|
||||
(
|
||||
json.dumps(
|
||||
{
|
||||
"live": live_session.to_dict()
|
||||
if live_session is not None
|
||||
else None,
|
||||
"active": result,
|
||||
}
|
||||
)
|
||||
+ "\r\n"
|
||||
json.dumps(
|
||||
{
|
||||
"live": live_session.to_dict()
|
||||
if live_session is not None
|
||||
else None,
|
||||
"active": result,
|
||||
}
|
||||
)
|
||||
+ "\r\n"
|
||||
).encode("utf-8")
|
||||
)
|
||||
|
||||
|
@ -444,7 +468,11 @@ async def run_telnet_server() -> None:
|
|||
|
||||
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(
|
||||
|
|
108
stateserver.py
108
stateserver.py
|
@ -6,7 +6,7 @@ import subprocess
|
|||
from typing import List, Any, Dict, Optional
|
||||
|
||||
from flask import Flask, jsonify, request
|
||||
from flask_cors import CORS # type: ignore
|
||||
from flask_cors import CORS # type: ignore
|
||||
import requests
|
||||
import datetime
|
||||
import random
|
||||
|
@ -25,7 +25,11 @@ 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))
|
||||
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:
|
||||
|
@ -45,7 +49,7 @@ def genPayload(payload: Any) -> 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:
|
||||
return res.json()["payload"]
|
||||
else:
|
||||
|
@ -85,7 +89,8 @@ wsSessions: Dict[str, Dict[str, str]] = {}
|
|||
def getCurrentShowConnection() -> Optional[Connection]:
|
||||
for connection in connections:
|
||||
if (connection["startTimestamp"] <= datetime.datetime.now().timestamp()) and (
|
||||
connection["endTimestamp"] >= getNextHourTimestamp()):
|
||||
connection["endTimestamp"] >= getNextHourTimestamp()
|
||||
):
|
||||
return connection
|
||||
return None
|
||||
|
||||
|
@ -109,8 +114,8 @@ def getNextHourConnection() -> Optional[Connection]:
|
|||
|
||||
def cleanOldConnections() -> None:
|
||||
global connections
|
||||
#Go backwards round the loop so that pop's don't interfere with the index.
|
||||
for i in range(len(connections)-1,-1,-1):
|
||||
# Go backwards round the loop so that pop's don't interfere with the index.
|
||||
for i in range(len(connections) - 1, -1, -1):
|
||||
if connections[i]["endTimestamp"] < datetime.datetime.now().timestamp():
|
||||
connections.pop(i)
|
||||
|
||||
|
@ -159,11 +164,15 @@ def stateDecider() -> Dict[str, Any]:
|
|||
newSelSource = currentConnection["sourceid"]
|
||||
newWSSource = currentConnection["wsid"]
|
||||
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
|
||||
newSelSource = SOURCE_JUKEBOX
|
||||
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
|
||||
newSelSource = SOURCE_JUKEBOX
|
||||
switchAudioAtMin = 0
|
||||
|
@ -173,32 +182,27 @@ def stateDecider() -> Dict[str, Any]:
|
|||
"autoNews": willRunAutoNews,
|
||||
"switchAudioAtMin": switchAudioAtMin,
|
||||
"selSource": newSelSource,
|
||||
"wsSource": newWSSource
|
||||
"wsSource": newWSSource,
|
||||
}
|
||||
|
||||
return nextState
|
||||
|
||||
|
||||
@app.route('/api/v1/status', methods=['GET'])
|
||||
@app.route("/api/v1/status", methods=["GET"])
|
||||
def get_status() -> Any:
|
||||
print(getNextHourTimestamp())
|
||||
global connections
|
||||
cleanOldConnections()
|
||||
return genPayload(
|
||||
{
|
||||
"connections": connections,
|
||||
"wsSessions": wsSessions
|
||||
}
|
||||
)
|
||||
return genPayload({"connections": connections, "wsSessions": wsSessions})
|
||||
|
||||
|
||||
@app.route('/api/v1/nextTransition', methods=['GET'])
|
||||
@app.route("/api/v1/nextTransition", methods=["GET"])
|
||||
def get_next_transition() -> Any:
|
||||
cleanOldConnections()
|
||||
return genPayload(stateDecider())
|
||||
|
||||
|
||||
@app.route('/api/v1/cancelTimeslot', methods=['POST'])
|
||||
@app.route("/api/v1/cancelTimeslot", methods=["POST"])
|
||||
def post_cancelCheck() -> Any:
|
||||
global connections
|
||||
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
|
||||
now = datetime.datetime.now().timestamp()
|
||||
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")
|
||||
subprocess.Popen(["sel", str(SOURCE_JUKEBOX)])
|
||||
|
||||
|
@ -227,7 +237,7 @@ def post_cancelCheck() -> Any:
|
|||
return genFail("Connection not found.")
|
||||
|
||||
|
||||
@app.route('/api/v1/registerTimeslot', methods=['POST'])
|
||||
@app.route("/api/v1/registerTimeslot", methods=["POST"])
|
||||
def post_registerCheck() -> Any:
|
||||
global connections
|
||||
|
||||
|
@ -274,7 +284,11 @@ def post_registerCheck() -> Any:
|
|||
for conn in connections:
|
||||
if content["timeslotid"] == conn["timeslotid"]:
|
||||
# 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
|
||||
# make sure we update their wsID
|
||||
if "wsid" in content:
|
||||
|
@ -282,37 +296,50 @@ def post_registerCheck() -> Any:
|
|||
|
||||
new_connection = False
|
||||
if connection is None:
|
||||
|
||||
new_connection = True
|
||||
|
||||
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:
|
||||
return genFail("This show has already ended.")
|
||||
|
||||
if 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.")
|
||||
if (
|
||||
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)
|
||||
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"],
|
||||
"startTimestamp": int(start_time.timestamp()),
|
||||
"endTimestamp": int(end_time.timestamp()),
|
||||
"sourceid": content["sourceid"],
|
||||
'autoNewsBeginning': True,
|
||||
'autoNewsMiddle': True,
|
||||
'autoNewsEnd': True,
|
||||
'wsid': content["wsid"]
|
||||
"autoNewsBeginning": True,
|
||||
"autoNewsMiddle": True,
|
||||
"autoNewsEnd": True,
|
||||
"wsid": content["wsid"],
|
||||
}
|
||||
|
||||
if start_time + datetime.timedelta(minutes=2) < now_time:
|
||||
if connection["wsid"] is not None:
|
||||
# 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"])
|
||||
subprocess.Popen(['sel', '5'])
|
||||
subprocess.Popen(["sel", "5"])
|
||||
|
||||
assert connection is not None
|
||||
if new_connection:
|
||||
|
@ -322,7 +349,7 @@ def post_registerCheck() -> Any:
|
|||
return genPayload(connection)
|
||||
|
||||
|
||||
@app.route('/api/v1/changeTimeslot', methods=['POST'])
|
||||
@app.route("/api/v1/changeTimeslot", methods=["POST"])
|
||||
def post_settingsCheck() -> Any:
|
||||
global connections
|
||||
content = request.json
|
||||
|
@ -349,11 +376,12 @@ def post_settingsCheck() -> Any:
|
|||
return genFail("No connection found.")
|
||||
|
||||
|
||||
@app.route('/api/v1/updateWSSessions', methods=['POST'])
|
||||
@app.route("/api/v1/updateWSSessions", methods=["POST"])
|
||||
def post_wsSessions() -> Any:
|
||||
global connections
|
||||
global wsSessions
|
||||
content = request.json
|
||||
assert content is not None
|
||||
# if not content:
|
||||
# return genFail("No parameters provided.")
|
||||
oldSessions = wsSessions
|
||||
|
@ -378,9 +406,13 @@ def post_wsSessions() -> Any:
|
|||
if conn["wsid"] in wsids_to_add:
|
||||
if conn["startTimestamp"] + 120 < datetime.datetime.now().timestamp():
|
||||
# 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"])
|
||||
subprocess.Popen(['sel', '5'])
|
||||
subprocess.Popen(["sel", "5"])
|
||||
|
||||
if conn["wsid"] in wsids_to_remove:
|
||||
print("({}, {}) gone".format(conn["connid"], conn["wsid"]))
|
||||
|
@ -394,10 +426,10 @@ def post_wsSessions() -> Any:
|
|||
now = datetime.datetime.now().timestamp()
|
||||
if now < (currentShow["endTimestamp"] - 15):
|
||||
print("jukeboxing due to their disappearance...")
|
||||
subprocess.Popen(['sel', str(SOURCE_JUKEBOX)])
|
||||
subprocess.Popen(["sel", str(SOURCE_JUKEBOX)])
|
||||
do_ws_srv_telnet("NUL")
|
||||
return genPayload("Thx, K, bye.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, host="0.0.0.0")
|
||||
|
|
Loading…
Reference in a new issue