From 65a560c3fcb960aa988f19465e8269b8bae21e94 Mon Sep 17 00:00:00 2001 From: Christoph Stahl Date: Sat, 8 Apr 2023 11:56:53 +0200 Subject: [PATCH] Added Waiting room by name --- syng/entry.py | 10 +++++++ syng/queue.py | 6 ++++ syng/server.py | 64 ++++++++++++++++++++++++++++++++--------- syng/sources/youtube.py | 36 ++++++++++++----------- 4 files changed, 86 insertions(+), 30 deletions(-) diff --git a/syng/entry.py b/syng/entry.py index accc5ac..a69684f 100644 --- a/syng/entry.py +++ b/syng/entry.py @@ -69,3 +69,13 @@ class Entry: :rtype: None """ self.__dict__.update(kwargs) + + def shares_performer(self, other_performer: str) -> bool: + e1_split_names = set( + filter(lambda x: len(x) > 3, self.performer.split(" ")) + ) + e2_split_names = set( + filter(lambda x: len(x) > 3, other_performer.split(" ")) + ) + + return len(e1_split_names.intersection(e2_split_names)) > 0 diff --git a/syng/queue.py b/syng/queue.py index 3a95454..ebc5fcc 100644 --- a/syng/queue.py +++ b/syng/queue.py @@ -107,6 +107,12 @@ class Queue: if item.uuid == uuid or str(item.uuid) == uuid: updater(item) + def find_by_name(self, name: str) -> Optional[Entry]: + for item in self._queue: + if item.shares_performer(name): + return item + return None + def find_by_uuid(self, uuid: UUID | str) -> Optional[Entry]: """ Find an entry by its uuid and return it. diff --git a/syng/server.py b/syng/server.py index e6edd82..4c544d0 100644 --- a/syng/server.py +++ b/syng/server.py @@ -180,6 +180,10 @@ async def handle_state(sid: str) -> None: @sio.on("waiting-room-append") async def handle_waiting_room_append(sid: str, data: dict[str, Any]) -> None: + """ + Append a song to the waiting room. + + """ async with sio.session(sid) as session: room = session["room"] state = clients[room] @@ -187,17 +191,28 @@ async def handle_waiting_room_append(sid: str, data: dict[str, Any]) -> None: print(data) source_obj = state.config.sources[data["source"]] + entry = await source_obj.get_entry(data["performer"], data["ident"]) + if entry is None: await sio.emit( "msg", - {"msg": f"Unable to add to the waiting room: {data['ident']}"}, + { + "msg": f"Unable to add to the waiting room: {data['ident']}. Maybe try again?" + }, + room=sid, ) return - if ( - "uid" not in data - or len(list(state.queue.find_by_uid(data["uid"]))) == 0 + if "uid" not in data or ( + ( + data["uid"] is not None + and len(list(state.queue.find_by_uid(data["uid"]))) == 0 + ) + or ( + data["uid"] is None + and state.queue.find_by_name(data["performer"]) is None + ) ): await append_to_queue(room, entry, sid) return @@ -213,10 +228,24 @@ async def handle_waiting_room_append(sid: str, data: dict[str, Any]) -> None: room=clients[room].sid, ) - # Und jetzt iwie hinzufügen, oder direkt queuen :/ +async def append_to_queue( + room: str, entry: Entry, report_to: Optional[str] = None +) -> None: + """ + Append an song to the queue for a given session. -async def append_to_queue(room, entry, report_to=None): + Checks, if the computed start time is before the configured end time of the + event, and reports an error, if the end time is exceeded. + + :param room: The room with the queue. + :type room: str + :param entry: The entry that contains the song. + :type entry: Entry + :param report_to: If an error occurs, who to report to. + :type report_to: Optional[str] + :rtype: None + """ state = clients[room] first_song = state.queue.try_peek() @@ -294,9 +323,15 @@ async def handle_append(sid: str, data: dict[str, Any]) -> None: state = clients[room] source_obj = state.config.sources[data["source"]] + entry = await source_obj.get_entry(data["performer"], data["ident"]) + if entry is None: - await sio.emit("msg", {"msg": f"Unable to append {data['ident']}"}) + await sio.emit( + "msg", + {"msg": f"Unable to append {data['ident']}. Maybe try again?"}, + room=sid, + ) return entry.uid = data["uid"] if "uid" in data else None @@ -367,12 +402,15 @@ async def handle_get_first(sid: str) -> None: await sio.emit("play", current, room=sid) -async def add_uid_from_waiting_room(uid, room) -> None: +async def add_song_from_waiting_room(old_entry: Entry, room: str) -> None: state = clients[room] first_entry_for_uid = None for wr_entry in state.waiting_room: - if wr_entry.uid == uid: + if wr_entry.uid == old_entry.uid or ( + wr_entry.uid == None + and wr_entry.shares_performer(old_entry.performer) + ): first_entry_for_uid = wr_entry break @@ -381,12 +419,12 @@ async def add_uid_from_waiting_room(uid, room) -> None: state.waiting_room.remove(first_entry_for_uid) -async def discard_first(room) -> Entry: +async def discard_first(room: str) -> Entry: state = clients[room] old_entry = await state.queue.popleft() - await add_uid_from_waiting_room(old_entry.uid, room) + await add_song_from_waiting_room(old_entry, room) state.recent.append(old_entry) state.last_seen = datetime.datetime.now() @@ -776,7 +814,7 @@ async def handle_skip(sid: str, data: dict[str, Any]) -> None: if entry is not None: logger.info("Skipping %s", entry) - await add_uid_from_waiting_room(entry.uid, room) + await add_song_from_waiting_room(entry, room) await state.queue.remove(entry) @@ -786,8 +824,6 @@ async def handle_skip(sid: str, data: dict[str, Any]) -> None: first_entry_index = idx break - print(first_entry_index) - if first_entry_index is not None: logger.info( "Deleting %s from waiting room", diff --git a/syng/sources/youtube.py b/syng/sources/youtube.py index a9dba0d..4c7cd5b 100644 --- a/syng/sources/youtube.py +++ b/syng/sources/youtube.py @@ -19,6 +19,7 @@ from pytube import Search from pytube import Stream from pytube import StreamQuery from pytube import YouTube +from pytube.exceptions import PytubeError try: from yt_dlp import YoutubeDL @@ -120,7 +121,7 @@ class YoutubeSource(Source): else: await super().play(entry) - async def get_entry(self, performer: str, ident: str) -> Entry: + async def get_entry(self, performer: str, ident: str) -> Optional[Entry]: """ Create an :py:class:`syng.entry.Entry` for the identifier. @@ -132,24 +133,27 @@ class YoutubeSource(Source): :param ident: A url to a YouTube video. :type ident: str :return: An entry with the data. - :rtype: Entry + :rtype: Optional[Entry] """ - def _get_entry(performer: str, url: str) -> Entry: - yt_song = YouTube(url) + def _get_entry(performer: str, url: str) -> Optional[Entry]: try: - length = yt_song.length - except TypeError: - length = 180 - return Entry( - ident=url, - source="youtube", - album="YouTube", - duration=length, - title=yt_song.title, - artist=yt_song.author, - performer=performer, - ) + yt_song = YouTube(url) + try: + length = yt_song.length + except TypeError: + length = 180 + return Entry( + ident=url, + source="youtube", + album="YouTube", + duration=length, + title=yt_song.title, + artist=yt_song.author, + performer=performer, + ) + except PytubeError: + return None return await asyncio.to_thread(_get_entry, performer, ident)