From 9685fe76a8f96565fb071c3bdb7e5a65dda21d7a Mon Sep 17 00:00:00 2001 From: Christoph Stahl Date: Mon, 14 Nov 2022 23:58:28 +0100 Subject: [PATCH] renamed short to room, fixed persistent room, argparse for client --- syng/client.py | 38 ++++++++--- syng/server.py | 137 ++++++++++++++++++++++------------------ syng/webclientmockup.py | 6 +- 3 files changed, 106 insertions(+), 75 deletions(-) diff --git a/syng/client.py b/syng/client.py index c96995e..adf8d09 100644 --- a/syng/client.py +++ b/syng/client.py @@ -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() diff --git a/syng/server.py b/syng/server.py index b949908..7462f6c 100644 --- a/syng/server.py +++ b/syng/server.py @@ -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) diff --git a/syng/webclientmockup.py b/syng/webclientmockup.py index a0112e5..42935ed 100644 --- a/syng/webclientmockup.py +++ b/syng/webclientmockup.py @@ -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")