renamed short to room, fixed persistent room, argparse for client
This commit is contained in:
parent
593ea380e3
commit
9685fe76a8
3 changed files with 106 additions and 75 deletions
|
@ -1,6 +1,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from traceback import print_exc
|
from traceback import print_exc
|
||||||
from json import load
|
from json import load
|
||||||
|
import logging
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
import socketio
|
import socketio
|
||||||
|
|
||||||
|
@ -9,21 +11,21 @@ from .entry import Entry
|
||||||
|
|
||||||
|
|
||||||
sio = socketio.AsyncClient()
|
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)
|
currentLock = asyncio.Semaphore(0)
|
||||||
state = {
|
state = {
|
||||||
"current": None,
|
"current": None,
|
||||||
"queue": [],
|
"queue": [],
|
||||||
|
"room": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@sio.on("skip")
|
@sio.on("skip")
|
||||||
async def handle_skip():
|
async def handle_skip():
|
||||||
print("Skipping current")
|
logger.info("Skipping current")
|
||||||
await state["current"].skip_current()
|
await state["current"].skip_current()
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,12 +36,13 @@ async def handle_state(data):
|
||||||
|
|
||||||
@sio.on("connect")
|
@sio.on("connect")
|
||||||
async def handle_connect():
|
async def handle_connect():
|
||||||
print("Connected to server")
|
logging.info("Connected to server")
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
"register-client",
|
"register-client",
|
||||||
{
|
{
|
||||||
"secret": "test",
|
"secret": "test",
|
||||||
"queue": [entry.to_dict() for entry in state["queue"]],
|
"queue": [entry.to_dict() for entry in state["queue"]],
|
||||||
|
"room": state["room"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +57,7 @@ async def handle_buffer(data):
|
||||||
@sio.on("play")
|
@sio.on("play")
|
||||||
async def handle_play(data):
|
async def handle_play(data):
|
||||||
entry = Entry(**data)
|
entry = Entry(**data)
|
||||||
print(f"Playing {entry}")
|
logging.info("Playing %s", entry)
|
||||||
try:
|
try:
|
||||||
meta_info = await sources[entry.source].buffer(entry)
|
meta_info = await sources[entry.source].buffer(entry)
|
||||||
await sio.emit("meta-info", {"uuid": data["uuid"], "meta": meta_info})
|
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)
|
await sources[entry.source].play(entry)
|
||||||
except Exception:
|
except Exception:
|
||||||
print_exc()
|
print_exc()
|
||||||
print("Finished, waiting for next")
|
logging.info("Finished, waiting for next")
|
||||||
await sio.emit("pop-then-get-next")
|
await sio.emit("pop-then-get-next")
|
||||||
|
|
||||||
|
|
||||||
@sio.on("client-registered")
|
@sio.on("client-registered")
|
||||||
async def handle_register(data):
|
async def handle_register(data):
|
||||||
if data["success"]:
|
if data["success"]:
|
||||||
print("Registered")
|
logging.info("Registered")
|
||||||
await sio.emit("sources", {"sources": list(source_config.keys())})
|
print(f"Join using code: {data['room']}")
|
||||||
|
state["room"] = data["room"]
|
||||||
|
await sio.emit("sources", {"sources": list(sources.keys())})
|
||||||
if state["current"] is None:
|
if state["current"] is None:
|
||||||
await sio.emit("get-first")
|
await sio.emit("get-first")
|
||||||
else:
|
else:
|
||||||
print("Registration failed")
|
logging.warning("Registration failed")
|
||||||
await sio.disconnect()
|
await sio.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,6 +104,19 @@ async def handle_request_config(data):
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
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.connect("http://127.0.0.1:8080")
|
||||||
await sio.wait()
|
await sio.wait()
|
||||||
|
|
||||||
|
|
137
syng/server.py
137
syng/server.py
|
@ -5,6 +5,7 @@ import asyncio
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import string
|
import string
|
||||||
import random
|
import random
|
||||||
|
import logging
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import socketio
|
import socketio
|
||||||
|
@ -12,42 +13,48 @@ import socketio
|
||||||
from .entry import Entry
|
from .entry import Entry
|
||||||
from .sources import Source, available_sources
|
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()
|
app = web.Application()
|
||||||
sio.attach(app)
|
sio.attach(app)
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
clients = {}
|
clients = {}
|
||||||
|
|
||||||
|
|
||||||
class Queue(deque):
|
class Queue:
|
||||||
def __init__(self, iterable=[]):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(iterable)
|
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()
|
self.readlock = asyncio.Lock()
|
||||||
|
|
||||||
async def append(self, item: Entry) -> None:
|
def append(self, x: Entry) -> None:
|
||||||
super().append(item)
|
self._queue.append(x)
|
||||||
self.num_of_entries_sem.release()
|
self.num_of_entries_sem.release()
|
||||||
|
|
||||||
async def peek(self) -> Entry:
|
async def peek(self) -> Entry:
|
||||||
async with self.readlock:
|
async with self.readlock:
|
||||||
await self.num_of_entries_sem.acquire()
|
await self.num_of_entries_sem.acquire()
|
||||||
item = super().popleft()
|
item = self._queue[0]
|
||||||
super().appendleft(item)
|
|
||||||
self.num_of_entries_sem.release()
|
self.num_of_entries_sem.release()
|
||||||
return item
|
return item
|
||||||
|
|
||||||
async def popleft(self) -> Entry:
|
async def popleft(self) -> Entry:
|
||||||
async with self.readlock:
|
async with self.readlock:
|
||||||
await self.num_of_entries_sem.acquire()
|
await self.num_of_entries_sem.acquire()
|
||||||
item = super().popleft()
|
item = self._queue.popleft()
|
||||||
await sio.emit("state", self.to_dict())
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def to_dict(self) -> list[dict[str, Any]]:
|
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
|
@dataclass
|
||||||
|
@ -62,8 +69,8 @@ class State:
|
||||||
@sio.on("get-state")
|
@sio.on("get-state")
|
||||||
async def handle_state(sid, data: dict[str, Any] = {}):
|
async def handle_state(sid, data: dict[str, Any] = {}):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
await sio.emit("state", state.queue.to_dict(), room=sid)
|
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")
|
@sio.on("append")
|
||||||
async def handle_append(sid, data: dict[str, Any]):
|
async def handle_append(sid, data: dict[str, Any]):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
print(f"append: {data}")
|
|
||||||
source_obj = state.sources[data["source"]]
|
source_obj = state.sources[data["source"]]
|
||||||
entry = await Entry.from_source(data["performer"], data["id"], source_obj)
|
entry = await Entry.from_source(data["performer"], data["id"], source_obj)
|
||||||
await state.queue.append(entry)
|
state.queue.append(entry)
|
||||||
await sio.emit("state", state.queue.to_dict(), room=short)
|
await sio.emit("state", state.queue.to_dict(), room=room)
|
||||||
|
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
"buffer",
|
"buffer",
|
||||||
entry.to_dict(),
|
entry.to_dict(),
|
||||||
room=clients[short].sid,
|
room=clients[room].sid,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@sio.on("meta-info")
|
@sio.on("meta-info")
|
||||||
async def handle_meta_info(sid, data):
|
async def handle_meta_info(sid, data):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
for item in state.queue:
|
state.queue.update(
|
||||||
if str(item.uuid) == data["uuid"]:
|
lambda item: str(item.uuid) == data["uuid"],
|
||||||
item.update(**data["meta"])
|
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")
|
@sio.on("get-first")
|
||||||
async def handle_get_first(sid, data={}):
|
async def handle_get_first(sid, data={}):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
current = await state.queue.peek()
|
current = await state.queue.peek()
|
||||||
|
|
||||||
|
@ -114,13 +124,13 @@ async def handle_get_first(sid, data={}):
|
||||||
@sio.on("pop-then-get-next")
|
@sio.on("pop-then-get-next")
|
||||||
async def handle_pop_then_get_next(sid, data={}):
|
async def handle_pop_then_get_next(sid, data={}):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
await state.queue.popleft()
|
await state.queue.popleft()
|
||||||
current = await state.queue.peek()
|
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)
|
await sio.emit("play", current.to_dict(), room=sid)
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,29 +143,30 @@ def gen_id(length=4) -> str:
|
||||||
|
|
||||||
@sio.on("register-client")
|
@sio.on("register-client")
|
||||||
async def handle_register_client(sid, data: dict[str, Any]):
|
async def handle_register_client(sid, data: dict[str, Any]):
|
||||||
print(f"Registerd new client {sid}")
|
room = data["room"] if "room" in data and data["room"] else gen_id()
|
||||||
short = data["short"] if "short" in data else gen_id()
|
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
session["short"] = short
|
session["room"] = room
|
||||||
|
|
||||||
print(f"short id: {short}")
|
if room in clients:
|
||||||
if data["short"] in clients:
|
old_state = clients[room]
|
||||||
old_state = clients[short]
|
|
||||||
if data["secret"] == old_state.secret:
|
if data["secret"] == old_state.secret:
|
||||||
|
logger.info("Got new client connection for %s", room)
|
||||||
old_state.sid = sid
|
old_state.sid = sid
|
||||||
sio.enter_room(sid, short)
|
sio.enter_room(sid, room)
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
"client-registered", {"success": True, "short": short}, room=sid
|
"client-registered", {"success": True, "room": room}, room=sid
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
logger.warning("Got wrong secret for %s", room)
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
"client-registered", {"success": False, "short": short}, room=sid
|
"client-registered", {"success": False, "room": room}, room=sid
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
logger.info("Registerd new client %s", room)
|
||||||
initial_entries = [Entry(**entry) for entry in data["queue"]]
|
initial_entries = [Entry(**entry) for entry in data["queue"]]
|
||||||
clients[short] = State(data["secret"], {}, [], Queue(initial_entries), sid)
|
clients[room] = State(data["secret"], {}, [], Queue(initial_entries), sid)
|
||||||
sio.enter_room(sid, short)
|
sio.enter_room(sid, room)
|
||||||
await sio.emit("client-registered", {"success": True, "short": short}, room=sid)
|
await sio.emit("client-registered", {"success": True, "room": room}, room=sid)
|
||||||
|
|
||||||
|
|
||||||
@sio.on("sources")
|
@sio.on("sources")
|
||||||
|
@ -166,8 +177,8 @@ async def handle_sources(sid, data):
|
||||||
sources and query for a config for all uninitialized sources
|
sources and query for a config for all uninitialized sources
|
||||||
"""
|
"""
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
unused_sources = state.sources.keys() - data["sources"]
|
unused_sources = state.sources.keys() - data["sources"]
|
||||||
new_sources = data["sources"] - state.sources.keys()
|
new_sources = data["sources"] - state.sources.keys()
|
||||||
|
@ -184,10 +195,11 @@ async def handle_sources(sid, data):
|
||||||
@sio.on("config-chunk")
|
@sio.on("config-chunk")
|
||||||
async def handle_config_chung(sid, data):
|
async def handle_config_chung(sid, data):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
if not data["source"] in state.sources:
|
if not data["source"] in state.sources:
|
||||||
|
logger.info("Added source %s", data["source"])
|
||||||
state.sources[data["source"]] = available_sources[data["source"]](
|
state.sources[data["source"]] = available_sources[data["source"]](
|
||||||
data["config"]
|
data["config"]
|
||||||
)
|
)
|
||||||
|
@ -198,19 +210,19 @@ async def handle_config_chung(sid, data):
|
||||||
@sio.on("config")
|
@sio.on("config")
|
||||||
async def handle_config(sid, data):
|
async def handle_config(sid, data):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
state.sources[data["source"]] = available_sources[data["source"]](data["config"])
|
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")
|
@sio.on("register-web")
|
||||||
async def handle_register_web(sid, data):
|
async def handle_register_web(sid, data):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
session["short"] = data["short"]
|
session["room"] = data["room"]
|
||||||
sio.enter_room(sid, session["short"])
|
sio.enter_room(sid, session["room"])
|
||||||
state = clients[session["short"]]
|
state = clients[session["room"]]
|
||||||
|
|
||||||
await sio.emit("state", state.queue.to_dict(), room=sid)
|
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")
|
@sio.on("register-admin")
|
||||||
async def handle_register_admin(sid, data: dict[str, str]):
|
async def handle_register_admin(sid, data: dict[str, str]):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
is_admin = data["secret"] == state.secret
|
is_admin = data["secret"] == state.secret
|
||||||
async with sio.session(sid) as session:
|
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")
|
@sio.on("get-config")
|
||||||
async def handle_get_config(sid, data):
|
async def handle_get_config(sid, data):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
is_admin = session["admin"]
|
is_admin = session["admin"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
if is_admin:
|
if is_admin:
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
|
@ -244,24 +256,24 @@ async def handle_get_config(sid, data):
|
||||||
@sio.on("skip")
|
@sio.on("skip")
|
||||||
async def handle_skip(sid, data={}):
|
async def handle_skip(sid, data={}):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
is_admin = session["admin"]
|
is_admin = session["admin"]
|
||||||
|
|
||||||
if is_admin:
|
if is_admin:
|
||||||
await sio.emit("skip", room=clients[short].sid)
|
await sio.emit("skip", room=clients[room].sid)
|
||||||
|
|
||||||
|
|
||||||
@sio.on("disconnect")
|
@sio.on("disconnect")
|
||||||
async def handle_disconnect(sid, data={}):
|
async def handle_disconnect(sid, data={}):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
sio.leave_room(sid, session["short"])
|
sio.leave_room(sid, session["room"])
|
||||||
|
|
||||||
|
|
||||||
@sio.on("search")
|
@sio.on("search")
|
||||||
async def handle_search(sid, data: dict[str, str]):
|
async def handle_search(sid, data: dict[str, str]):
|
||||||
async with sio.session(sid) as session:
|
async with sio.session(sid) as session:
|
||||||
short = session["short"]
|
room = session["room"]
|
||||||
state = clients[short]
|
state = clients[room]
|
||||||
|
|
||||||
query = data["query"]
|
query = data["query"]
|
||||||
result_futures = []
|
result_futures = []
|
||||||
|
@ -276,7 +288,6 @@ async def handle_search(sid, data: dict[str, str]):
|
||||||
for result_future in result_futures
|
for result_future in result_futures
|
||||||
for search_result in await result_future
|
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)
|
await sio.emit("search-results", [result.to_dict() for result in results], room=sid)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from .entry import Entry
|
||||||
from aiocmd import aiocmd
|
from aiocmd import aiocmd
|
||||||
|
|
||||||
sio = socketio.AsyncClient()
|
sio = socketio.AsyncClient()
|
||||||
|
state = {}
|
||||||
|
|
||||||
|
|
||||||
@sio.on("search-results")
|
@sio.on("search-results")
|
||||||
|
@ -26,6 +27,7 @@ async def handle_state(data):
|
||||||
@sio.on("connect")
|
@sio.on("connect")
|
||||||
async def handle_connect():
|
async def handle_connect():
|
||||||
print("Connected")
|
print("Connected")
|
||||||
|
await sio.emit("register-web", {"room": state["room"]})
|
||||||
|
|
||||||
|
|
||||||
@sio.on("register-admin")
|
@sio.on("register-admin")
|
||||||
|
@ -61,9 +63,9 @@ class SyngShell(aiocmd.PromptToolkitCmd):
|
||||||
async def do_admin(self, data):
|
async def do_admin(self, data):
|
||||||
await sio.emit("register-admin", {"secret": 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.connect("http://127.0.0.1:8080")
|
||||||
await sio.emit("register-web", {"short": short})
|
|
||||||
|
|
||||||
async def do_skip(self):
|
async def do_skip(self):
|
||||||
await sio.emit("skip")
|
await sio.emit("skip")
|
||||||
|
|
Loading…
Add table
Reference in a new issue