renamed short to room, fixed persistent room, argparse for client

This commit is contained in:
Christoph Stahl 2022-11-14 23:58:28 +01:00
parent 593ea380e3
commit 9685fe76a8
3 changed files with 106 additions and 75 deletions

View file

@ -1,6 +1,8 @@
import asyncio
from traceback import print_exc
from json import load
import logging
from argparse import ArgumentParser
import socketio
@ -9,21 +11,21 @@ from .entry import Entry
sio = socketio.AsyncClient()
logger = logging.getLogger(__name__)
sources: dict[str, Source] = {}
with open("./syng-client.json", encoding="utf8") as f:
source_config = load(f)
sources: dict[str, Source] = configure_sources(source_config)
currentLock = asyncio.Semaphore(0)
state = {
"current": None,
"queue": [],
"room": None,
}
@sio.on("skip")
async def handle_skip():
print("Skipping current")
logger.info("Skipping current")
await state["current"].skip_current()
@ -34,12 +36,13 @@ async def handle_state(data):
@sio.on("connect")
async def handle_connect():
print("Connected to server")
logging.info("Connected to server")
await sio.emit(
"register-client",
{
"secret": "test",
"queue": [entry.to_dict() for entry in state["queue"]],
"room": state["room"],
},
)
@ -54,7 +57,7 @@ async def handle_buffer(data):
@sio.on("play")
async def handle_play(data):
entry = Entry(**data)
print(f"Playing {entry}")
logging.info("Playing %s", entry)
try:
meta_info = await sources[entry.source].buffer(entry)
await sio.emit("meta-info", {"uuid": data["uuid"], "meta": meta_info})
@ -62,19 +65,21 @@ async def handle_play(data):
await sources[entry.source].play(entry)
except Exception:
print_exc()
print("Finished, waiting for next")
logging.info("Finished, waiting for next")
await sio.emit("pop-then-get-next")
@sio.on("client-registered")
async def handle_register(data):
if data["success"]:
print("Registered")
await sio.emit("sources", {"sources": list(source_config.keys())})
logging.info("Registered")
print(f"Join using code: {data['room']}")
state["room"] = data["room"]
await sio.emit("sources", {"sources": list(sources.keys())})
if state["current"] is None:
await sio.emit("get-first")
else:
print("Registration failed")
logging.warning("Registration failed")
await sio.disconnect()
@ -99,6 +104,19 @@ async def handle_request_config(data):
async def main():
parser = ArgumentParser()
parser.add_argument("--room", "-r")
parser.add_argument("config")
args = parser.parse_args()
with open(args.config, encoding="utf8") as file:
source_config = load(file)
sources.update(configure_sources(source_config))
if args.room:
state["room"] = args.room
await sio.connect("http://127.0.0.1:8080")
await sio.wait()

View file

@ -5,6 +5,7 @@ import asyncio
from dataclasses import dataclass
import string
import random
import logging
from aiohttp import web
import socketio
@ -12,42 +13,48 @@ import socketio
from .entry import Entry
from .sources import Source, available_sources
sio = socketio.AsyncServer(cors_allowed_origins="*", logger=True, engineio_logger=True)
sio = socketio.AsyncServer(cors_allowed_origins="*", logger=True, engineio_logger=False)
app = web.Application()
sio.attach(app)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
clients = {}
class Queue(deque):
def __init__(self, iterable=[]):
super().__init__(iterable)
class Queue:
def __init__(self, *args, **kwargs):
self._queue = deque(*args, **kwargs)
self.num_of_entries_sem = asyncio.Semaphore(len(iterable))
self.num_of_entries_sem = asyncio.Semaphore(len(self._queue))
self.readlock = asyncio.Lock()
async def append(self, item: Entry) -> None:
super().append(item)
def append(self, x: Entry) -> None:
self._queue.append(x)
self.num_of_entries_sem.release()
async def peek(self) -> Entry:
async with self.readlock:
await self.num_of_entries_sem.acquire()
item = super().popleft()
super().appendleft(item)
item = self._queue[0]
self.num_of_entries_sem.release()
return item
async def popleft(self) -> Entry:
async with self.readlock:
await self.num_of_entries_sem.acquire()
item = super().popleft()
await sio.emit("state", self.to_dict())
item = self._queue.popleft()
return item
def to_dict(self) -> list[dict[str, Any]]:
return [item.to_dict() for item in self]
return [item.to_dict() for item in self._queue]
def update(self, locator, updater):
for item in self._queue:
if locator(item):
updater(item)
@dataclass
@ -62,8 +69,8 @@ class State:
@sio.on("get-state")
async def handle_state(sid, data: dict[str, Any] = {}):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
await sio.emit("state", state.queue.to_dict(), room=sid)
@ -71,40 +78,43 @@ async def handle_state(sid, data: dict[str, Any] = {}):
@sio.on("append")
async def handle_append(sid, data: dict[str, Any]):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
print(f"append: {data}")
source_obj = state.sources[data["source"]]
entry = await Entry.from_source(data["performer"], data["id"], source_obj)
await state.queue.append(entry)
await sio.emit("state", state.queue.to_dict(), room=short)
state.queue.append(entry)
await sio.emit("state", state.queue.to_dict(), room=room)
await sio.emit(
"buffer",
entry.to_dict(),
room=clients[short].sid,
room=clients[room].sid,
)
@sio.on("meta-info")
async def handle_meta_info(sid, data):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
for item in state.queue:
if str(item.uuid) == data["uuid"]:
item.update(**data["meta"])
state.queue.update(
lambda item: str(item.uuid) == data["uuid"],
lambda item: item.update(**data["meta"]),
)
# for item in state.queue:
# if str(item.uuid) == data["uuid"]:
# item.update(**data["meta"])
await sio.emit("state", state.queue.to_dict(), room=short)
await sio.emit("state", state.queue.to_dict(), room=room)
@sio.on("get-first")
async def handle_get_first(sid, data={}):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
current = await state.queue.peek()
@ -114,13 +124,13 @@ async def handle_get_first(sid, data={}):
@sio.on("pop-then-get-next")
async def handle_pop_then_get_next(sid, data={}):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
await state.queue.popleft()
current = await state.queue.peek()
await sio.emit("state", state.queue.to_dict(), room=short)
await sio.emit("state", state.queue.to_dict(), room=room)
await sio.emit("play", current.to_dict(), room=sid)
@ -133,29 +143,30 @@ def gen_id(length=4) -> str:
@sio.on("register-client")
async def handle_register_client(sid, data: dict[str, Any]):
print(f"Registerd new client {sid}")
short = data["short"] if "short" in data else gen_id()
room = data["room"] if "room" in data and data["room"] else gen_id()
async with sio.session(sid) as session:
session["short"] = short
session["room"] = room
print(f"short id: {short}")
if data["short"] in clients:
old_state = clients[short]
if room in clients:
old_state = clients[room]
if data["secret"] == old_state.secret:
logger.info("Got new client connection for %s", room)
old_state.sid = sid
sio.enter_room(sid, short)
sio.enter_room(sid, room)
await sio.emit(
"client-registered", {"success": True, "short": short}, room=sid
"client-registered", {"success": True, "room": room}, room=sid
)
else:
logger.warning("Got wrong secret for %s", room)
await sio.emit(
"client-registered", {"success": False, "short": short}, room=sid
"client-registered", {"success": False, "room": room}, room=sid
)
else:
logger.info("Registerd new client %s", room)
initial_entries = [Entry(**entry) for entry in data["queue"]]
clients[short] = State(data["secret"], {}, [], Queue(initial_entries), sid)
sio.enter_room(sid, short)
await sio.emit("client-registered", {"success": True, "short": short}, room=sid)
clients[room] = State(data["secret"], {}, [], Queue(initial_entries), sid)
sio.enter_room(sid, room)
await sio.emit("client-registered", {"success": True, "room": room}, room=sid)
@sio.on("sources")
@ -166,8 +177,8 @@ async def handle_sources(sid, data):
sources and query for a config for all uninitialized sources
"""
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
unused_sources = state.sources.keys() - data["sources"]
new_sources = data["sources"] - state.sources.keys()
@ -184,10 +195,11 @@ async def handle_sources(sid, data):
@sio.on("config-chunk")
async def handle_config_chung(sid, data):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
if not data["source"] in state.sources:
logger.info("Added source %s", data["source"])
state.sources[data["source"]] = available_sources[data["source"]](
data["config"]
)
@ -198,19 +210,19 @@ async def handle_config_chung(sid, data):
@sio.on("config")
async def handle_config(sid, data):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
state.sources[data["source"]] = available_sources[data["source"]](data["config"])
print(f"Added source {data['source']}")
logger.info("Added source %s", data["source"])
@sio.on("register-web")
async def handle_register_web(sid, data):
async with sio.session(sid) as session:
session["short"] = data["short"]
sio.enter_room(sid, session["short"])
state = clients[session["short"]]
session["room"] = data["room"]
sio.enter_room(sid, session["room"])
state = clients[session["room"]]
await sio.emit("state", state.queue.to_dict(), room=sid)
@ -218,8 +230,8 @@ async def handle_register_web(sid, data):
@sio.on("register-admin")
async def handle_register_admin(sid, data: dict[str, str]):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
is_admin = data["secret"] == state.secret
async with sio.session(sid) as session:
@ -230,9 +242,9 @@ async def handle_register_admin(sid, data: dict[str, str]):
@sio.on("get-config")
async def handle_get_config(sid, data):
async with sio.session(sid) as session:
short = session["short"]
room = session["room"]
is_admin = session["admin"]
state = clients[short]
state = clients[room]
if is_admin:
await sio.emit(
@ -244,24 +256,24 @@ async def handle_get_config(sid, data):
@sio.on("skip")
async def handle_skip(sid, data={}):
async with sio.session(sid) as session:
short = session["short"]
room = session["room"]
is_admin = session["admin"]
if is_admin:
await sio.emit("skip", room=clients[short].sid)
await sio.emit("skip", room=clients[room].sid)
@sio.on("disconnect")
async def handle_disconnect(sid, data={}):
async with sio.session(sid) as session:
sio.leave_room(sid, session["short"])
sio.leave_room(sid, session["room"])
@sio.on("search")
async def handle_search(sid, data: dict[str, str]):
async with sio.session(sid) as session:
short = session["short"]
state = clients[short]
room = session["room"]
state = clients[room]
query = data["query"]
result_futures = []
@ -276,7 +288,6 @@ async def handle_search(sid, data: dict[str, str]):
for result_future in result_futures
for search_result in await result_future
]
print(f"Found {len(results)} results")
await sio.emit("search-results", [result.to_dict() for result in results], room=sid)

View file

@ -5,6 +5,7 @@ from .entry import Entry
from aiocmd import aiocmd
sio = socketio.AsyncClient()
state = {}
@sio.on("search-results")
@ -26,6 +27,7 @@ async def handle_state(data):
@sio.on("connect")
async def handle_connect():
print("Connected")
await sio.emit("register-web", {"room": state["room"]})
@sio.on("register-admin")
@ -61,9 +63,9 @@ class SyngShell(aiocmd.PromptToolkitCmd):
async def do_admin(self, data):
await sio.emit("register-admin", {"secret": data})
async def do_connect(self, short):
async def do_connect(self, room):
state["room"] = room
await sio.connect("http://127.0.0.1:8080")
await sio.emit("register-web", {"short": short})
async def do_skip(self):
await sio.emit("skip")