| 
					
				 | 
			
			
				@@ -6,6 +6,7 @@ from typing import Optional, Iterator, Dict, List 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import sys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from itertools import cycle 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from websockets.asyncio.server import ServerConnection 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import chube_youtube 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from channel import Channel, Subscriber 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -131,7 +132,10 @@ class Playback: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def set_song(self, song): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         with self.lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self._song = song 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            logger.debug("Playback %s: Set song to %d", self, song["id"]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if song is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logger.debug("Playback %s: Set song to %d", self, song["id"]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logger.debug("Playback %s: finished last song", self) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def get_song(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         with self.lock: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -177,7 +181,7 @@ class Room: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 rooms: Dict[str, Room] = dict() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-async def request_state_processor(ws, _, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def request_state_processor(ws: ServerConnection, _, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room = rooms[path] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     state = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         "lists": room.chueue.as_lists(), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -290,10 +294,10 @@ async def obtain_control_processor(ws, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     await obtain_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-async def release_control_processor(ws, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def release_control_processor(ws: ServerConnection, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room = rooms[path] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if len(room.channel.subscribers) > 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        await release_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await release_control(ws, False, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -329,12 +333,12 @@ async def player_enabled_processor(ws, data, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if room.get_controller() is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 await obtain_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        await release_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await release_control(ws, False, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # TODO change OBTAIN_CONTROL en RELEASE_CONTROL to one message 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # 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: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def obtain_control(ws: ServerConnection, room: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     with room.controller_lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         controller = room.get_controller() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if controller is None or controller.ws is not ws: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -344,7 +348,7 @@ async def obtain_control(ws, room: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 await controller.ws.send(make_message(Message.RELEASE_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-async def release_control(ws, room: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def release_control(ws: ServerConnection, ws_disconnected: bool, room: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     with room.controller_lock: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         controller = room.get_controller() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if controller is not None and controller.ws is ws: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -352,8 +356,8 @@ async def release_control(ws, room: Room): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             room.set_controller(controller) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if controller is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 await controller.ws.send(make_message(Message.OBTAIN_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            # if ws.open: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            await ws.send(make_message(Message.RELEASE_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if not ws_disconnected: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                await ws.send(make_message(Message.RELEASE_CONTROL)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 async def on_connect(ws, path): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -371,7 +375,7 @@ async def on_connect(ws, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 async def on_disconnect(ws, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room = rooms[path] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     room.channel.unsubscribe(ws) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    await release_control(ws, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    await release_control(ws, True, room) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     print("Currently {} user{} {} using room {}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         len(room.channel.subscribers), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         "s" if len(room.channel.subscribers) != 1 else "", 
			 |