| 
					
				 | 
			
			
				@@ -6,7 +6,7 @@ from itertools import cycle 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from websockets import WebSocketServerProtocol 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import chube_search 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from channel import Channel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from channel import Channel, Subscriber 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from chube_enums import * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from chube_ws import Resolver, Message, start_server, make_message 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -111,7 +111,7 @@ class Playback: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class Room: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     chueue: Chueue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     channel: Channel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    controller: Optional[WebSocketServerProtocol] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    controller: Optional[Subscriber] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     controller_lock: RLock 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     playback: Playback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -123,8 +123,7 @@ class Room: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.controller = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-rooms = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+rooms: Dict[str, Room] = dict() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 async def request_state_processor(ws, data, path): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -183,7 +182,7 @@ async def song_end_processor(ws, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room = rooms[path] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     old_song_id = data["id"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     with room.controller_lock, room.playback.lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if ws is room.controller and old_song_id == room.playback.get_song_id(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if room.controller is not None and ws is room.controller.ws and old_song_id == room.playback.get_song_id(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             new_song = room.chueue.pop() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             room.playback.set_song(new_song) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if new_song is None: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -191,33 +190,42 @@ async def song_end_processor(ws, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 new_song_id = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 new_song_id = new_song["id"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            await room.channel.send(make_message(Message.SONG_END, {"ended_id": old_song_id, "current_id": new_song_id})) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            await room.channel.send( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_message(Message.SONG_END, {"ended_id": old_song_id, "current_id": new_song_id})) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 async def playback_processor(ws, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room = rooms[path] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def player_enabled_processor(ws, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    room = rooms[path] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    room.channel.subscribers[ws].player_enabled = data["enabled"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if data["enabled"]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        with room.controller_lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if room.controller is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                await obtain_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await release_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # TODO There is some potential concurrent bug here, when the controller loses/releases control right before a song end. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-async def obtain_control(ws, room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def obtain_control(ws, room: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     with room.controller_lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if room.controller is not ws: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if room.controller is None or room.controller.ws is not ws: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             prev_controller = room.controller 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            room.controller = ws 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            room.controller = room.channel.subscribers[ws] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             await ws.send(make_message(Message.OBTAIN_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if prev_controller is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                await prev_controller.send(make_message(Message.RELEASE_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                await prev_controller.ws.send(make_message(Message.RELEASE_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-async def release_control(ws, room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def release_control(ws, room: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     with room.controller_lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if room.controller is ws: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            subs = room.channel.subscribers 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if len(subs) > 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                room.controller = subs[0] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                await room.controller.send(make_message(Message.OBTAIN_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                room.controller = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if room.controller is not None and room.controller.ws is ws: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            room.controller = next(room.channel.get_player_enabled_subscribers(), None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if room.controller is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                await room.controller.ws.send(make_message(Message.OBTAIN_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             await ws.send(make_message(Message.RELEASE_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -226,14 +234,6 @@ async def on_connect(ws, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         rooms[path] = Room() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room = rooms[path] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room.channel.subscribe(ws) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    with room.controller_lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if room.controller is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            await obtain_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            # with room.playback.lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            #     if room.playback.get_state() == PlayerState.WAITING_FOR_CLIENTS: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            #         room.playback.set_state(PlayerState.PLAYING) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            #         # TODO maybe send a play message on channel. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            #         # await ws.send(make_message(Message.MEDIA_ACTION, {"action": MediaAction.PLAY})) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 async def on_disconnect(ws, path): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -249,6 +249,7 @@ def make_resolver(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     resolver = Resolver() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     resolver.register(Message.STATE, request_state_processor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     resolver.register(Message.LIST_OPERATION, request_list_operation_processor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    resolver.register(Message.PLAYER_ENABLED, player_enabled_processor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     resolver.register(Message.OBTAIN_CONTROL, obtain_control_processor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     resolver.register(Message.RELEASE_CONTROL, release_control_processor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     resolver.register(Message.SONG_END, song_end_processor) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -264,6 +265,7 @@ def init_rooms(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     # rooms["main"] = Room() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 if __name__ == "__main__": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     player_resolver = make_resolver() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |