class TorrentPeerProcessor(TorrentManager): def __init__(self, torrent): TorrentManager.__init__(self, torrent, "Peer processor") self.running = False self.process_thread = CustomThread(self.process, "Peer processor") def start(self): self.running = True self.process_thread.start() def stop(self): self.running = False self.process_thread.join() super().stop() def process(self): while self.running: start_time = current_time() peers_to_process = self.torrent.peer_manager.connected_peers Timing().start_timing("peer_processing") for peer in peers_to_process: peer.metadata_manager.update() peer.download_manager.update_timeout() Timing().stop_timing("peer_processing") spend_time = current_time() - start_time time.sleep(0.1 - (spend_time / 1000))
def start(self): if not self.pi: return self.cec_process = subprocess.Popen(['cec-client'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) t = CustomThread(self.__read_cec, "Cec reader", []) t.start()
def search_subtitles(self, name, size, length, first_64k, last_64k): Logger().write( LogVerbosity.Info, "Going to search subs: name: " + name + ", size: " + str(size) + ", length: " + str(length)) self.sub_files = [] for source in self.subtitle_sources: thread = CustomThread( self.search_subtitles_thread, "Search subtitles", [source, size, length, name, first_64k, last_64k]) thread.start()
class PresenceManager(metaclass=Singleton): def __init__(self): self.check_thread = CustomThread(self.check_presence, "Presence checker") self.running = False self.check_interval = 5 self.device_gone_interval = 240 self.on_coming_home = None self.on_leaving_home = None self.anyone_home = True self.pi = sys.platform == "linux" or sys.platform == "linux2" self.device_states = [ DeviceState("Mobiel Jan", "192.168.2.51", self.device_gone_interval), DeviceState("Mobiel Melissa", "192.168.2.50", self.device_gone_interval), ] def start(self): self.running = True self.check_thread.start() def stop(self): self.running = False self.check_thread.join() def check_presence(self): if not self.pi: return while self.running: for device in self.device_states: result = subprocess.call('sudo arping -q -c1 -W 1 ' + device.ip + ' > /dev/null', shell=True) device.set_device_state(result == 0) if self.anyone_home and len( [x for x in self.device_states if x.home_state]) == 0: # left self.anyone_home = False Logger().write(LogVerbosity.Info, "Everybody left home") if self.on_leaving_home is not None: self.on_leaving_home() elif not self.anyone_home and len( [x for x in self.device_states if x.home_state]) > 0: # returned self.anyone_home = True Logger().write(LogVerbosity.Info, "Someone came home") if self.on_coming_home is not None: self.on_coming_home() time.sleep(self.check_interval)
class TorrentNetworkManager(TorrentManager): def __init__(self, torrent): super().__init__(torrent, "network") self.running = True self.torrent = torrent self.speed_log = 0 self.average_download_counter = AverageCounter(self, 3) self.thread = None self._event_id_stopped = EventManager.register_event(EventType.TorrentStopped, self.unregister) def unregister(self): EventManager.deregister_event(self._event_id_stopped) def start(self): Logger().write(LogVerbosity.Info, "Starting network manager") self.thread = CustomThread(self.execute, "Network IO") self.thread.start() def execute(self): while self.running: Timing().start_timing("IO") # Select in/outputs input_peers = self.torrent.peer_manager.get_peers_for_reading() received_messages = [] for peer in input_peers: messages_size, messages = peer.connection_manager.handle_read() if messages_size > 0: self.average_download_counter.add_value(messages_size) time = current_time() received_messages += [(peer, x, time) for x in messages] if len(received_messages) != 0: self.torrent.message_processor.process_messages(received_messages) for peer in self.torrent.peer_manager.connected_peers: peer.download_manager.update_requests() output_peers = self.torrent.peer_manager.get_peers_for_writing() for peer in output_peers: peer.connection_manager.handle_write() Timing().stop_timing("IO") sleep(0.005) def stop(self): self.running = False self.thread.join() super().stop()
def request_peers(self, torrent): if not self.initialized: for uri in torrent.announce_uris: if len([x for x in self.trackers if x.host == uri]) == 0: tracker = TrackerFactory.create_tracker(uri) if tracker is not None: self.trackers.append(tracker) Logger().write(LogVerbosity.Debug, "Initialized " + str(len(self.trackers)) + " trackers") self.initialized = True for tracker in self.trackers: thread = CustomThread(self.tracker_announce, "Tracker announce", [tracker, torrent]) thread.start()
class StateManager(metaclass=Singleton): def __init__(self): self.state_data = StateData() self.monitoring = sys.platform == "linux" or sys.platform == "linux2" self.state_data.name = Settings.get_string("name") self.watch_thread = CustomThread(self.update_state, "State observer") self.watch_thread.start() self.memory_thread = CustomThread(self.check_memory, "Memory observer") self.memory_thread.start() def update_state(self): boot_time = psutil.boot_time() while True: self.state_data.start_update() self.state_data.memory = psutil.virtual_memory().percent self.state_data.cpu = psutil.cpu_percent() self.state_data.threads = ThreadManager().thread_count self.state_data.temperature = self.get_temperature() disk_info = psutil.disk_usage("/" if self.monitoring else "C:/") self.state_data.disk_total = disk_info.total self.state_data.disk_used = disk_info.used self.state_data.disk_free = disk_info.free self.state_data.disk_percentage = disk_info.percent self.state_data.boot_time = boot_time self.state_data.stop_update() time.sleep(1) def check_memory(self): if not self.monitoring: return while True: if self.state_data.memory > 90: Logger().write( LogVerbosity.Info, "Memory high ( " + str(self.state_data.memory) + "% )") time.sleep(10) def get_temperature(self): if not self.monitoring: return "-" proc = subprocess.Popen(["vcgencmd", "measure_temp"], stdout=PIPE, universal_newlines=True) out, err = proc.communicate() return out.replace("temp=", "")
def start(self): APIController.slaves = SlaveCollection() log_verbosity = Settings.get_int("log_level") if log_verbosity > 0: flask_logger = logging.getLogger('werkzeug') flask_logger.setLevel(logging.INFO) if Settings.get_bool("slave"): thread = CustomThread(self.internal_start_slave, "API controller", []) thread.start() else: thread = CustomThread(self.internal_start_master, "API controller", []) thread.start()
def start(self): Logger().write( LogVerbosity.Debug, self.name + " starting listener on port " + str(self.port)) self.soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.running = True try: self.soc.bind(("", self.port)) Logger().write( LogVerbosity.Info, "StreamServer " + self.name + " listening on port " + str(self.port)) except (socket.error, OSError) as e: Logger().write( LogVerbosity.Info, "Couldn't start StreamServer " + self.name + ": " + str(e)) return self.soc.listen(10) try: while True: Logger().write( LogVerbosity.Debug, "StreamServer " + self.name + " listening for incoming connection") conn, addr = self.soc.accept() if not self.running: break ip, port = str(addr[0]), str(addr[1]) Logger().write(LogVerbosity.Debug, 'New connection from ' + ip + ':' + port) thread = CustomThread(self.client_thread, "Stream request", [conn]) thread.start() except Exception as e: Logger().write_error(e, "Stream server") Logger().write(LogVerbosity.Debug, "StreamServer " + self.name + " closing") self.soc.close()
class Stats(metaclass=Singleton): def __init__(self): self.cache = StatList() self.changed = False self.work_thread = CustomThread(self.save_stats, "Stat saver", []) def start(self): stats = Database().get_stats() for key, value, last_change in stats: self.cache.update(key, value) self.work_thread.start() def _update_stat(self, name, value): self.cache.update(name, value) self.changed = True def save_stats(self): while True: if self.changed: self.changed = False copy = self.cache.statistics.copy() Logger().write(LogVerbosity.Debug, "Saving stats") for key, val in copy.items(): Database().update_stat(key, val) time.sleep(15) def add(self, name, value): stat = self.cache.get(name) if stat == 0: self._update_stat(name, value) else: self._update_stat(name, stat + value) def total(self, name): return self.cache.get(name) def set(self, name, value): self._update_stat(name, value)
def on_command(topic, command, args): Logger().write(LogVerbosity.Debug, "Master command " + topic + ": " + command) method = None if topic == "media": from MediaPlayer.MediaManager import MediaManager method = getattr(MediaManager(), command) if topic == "updater": from Updater import Updater method = getattr(Updater(), command) if topic == "system": if command == "restart_device": os.system('sudo reboot') if command == "restart_application": python = sys.executable os.execl(python, python, *sys.argv) if command == "close_application": sys.exit() if method is not None: cb_thread = CustomThread(method, "Master command", args) cb_thread.start()
def stop(self): Logger().write(LogVerbosity.All, "Player stop") thread = CustomThread(lambda: self.__player.stop(), "Stopping VLC player") thread.start()
class StreamListener(LogObject): wait_for_data = 0.1 mime_mapping = { ".mp4": "video/mp4", ".avi": "video/x-msvideo", ".mkv": "video/mp4", ".srt": "json" } def __init__(self, name, port, arg=None): super().__init__(arg, name) self.name = name self.torrent = arg self.port = port self.thread = None self.chunk_length = Settings.get_int("max_chunk_size") self.server = StreamServer(self.name, port, self.handle_request) self.sockets_writing_data = [] self.running = False self.bytes_send = 0 self.id = 0 def start_listening(self): self.thread = CustomThread(self.server.start, "Listener: " + self.name) self.running = True self.thread.start() def handle_request(self, socket): Logger().write(LogVerbosity.Info, self.name + " new request") # Read headers total_message = self.read_headers(socket) if total_message is None: return header = HttpHeader.from_string(total_message) if header.path == "/torrent": # Handle torrent stream request self.handle_torrent_request(socket, header) elif header.path.startswith("/file"): # Handle file stream request self.handle_file_request(socket, header) else: # Unknown request Logger().write( LogVerbosity.Info, self.name + " streamListener received unknown request: " + header.path) socket.close() def read_headers(self, socket): try: total_message = b'' while not total_message.endswith(b'\r\n\r\n'): rec = socket.recv(1024) if len(rec) == 0: break total_message += rec time.sleep(0) except (socket.timeout, ConnectionRefusedError, ConnectionAbortedError, ConnectionResetError, OSError): socket.close() Logger().write(LogVerbosity.Info, self.name + " error reading http header") return if not total_message.endswith(b'\r\n\r\n'): socket.close() Logger().write(LogVerbosity.Info, self.name + " invalid http header, closing") return return total_message def handle_file_request(self, socket, header): file_path = header.path[6:] if sys.platform == "linux" or sys.platform == "linux2": file_path = "/" + file_path Logger().write(LogVerbosity.Debug, self.name + " file request for " + file_path) if not os.path.exists(file_path): file_path = urllib.parse.unquote_plus(file_path) if not os.path.exists(file_path): Logger().write(LogVerbosity.Info, self.name + " file not found: " + file_path) self.write_header(socket, "404 Not Found") socket.close() return read_file = ReadFile(file_path) read_file.open() if header.range_end == 0 or header.range_end == -1: header.range_end = read_file.size - 1 if header.range is None: Logger().write(LogVerbosity.Debug, self.name + ' request without range') self.write_header_with_content(socket, "200 OK", 0, header.range_end, read_file.size, file_path) self.write_data(socket, header.range_start, header.range_end - header.range_start + 1, read_file.get_bytes) else: Logger().write(LogVerbosity.Debug, self.name + ' request with range') self.write_header_with_content(socket, "206 Partial Content", header.range_start, header.range_end, read_file.size, file_path) self.write_data(socket, header.range_start, header.range_end - header.range_start + 1, read_file.get_bytes) read_file.close() def handle_torrent_request(self, socket, header): if not self.torrent or not self.running: socket.close() Logger().write( LogVerbosity.Debug, self.name + " stopping connection because there is no more torrent") return if header.range_end == 0 or header.range_end == -1: header.range_end = self.torrent.media_file.length - 1 if header.range: range_start = header.range_start if range_start == self.torrent.media_file.length: Logger().write(LogVerbosity.Debug, "Request for content length 0, cant process") self.write_header(socket, "416 Requested range not satisfiable") socket.close() return if header.range is None: Logger().write(LogVerbosity.Debug, self.name + ' request without range') success = self.write_header_with_content( socket, "200 OK", 0, header.range_end, self.torrent.media_file.length, self.torrent.media_file.path) if not success: return self.write_data(socket, header.range_start, header.range_end - header.range_start + 1, self.torrent.get_data) else: Logger().write(LogVerbosity.Debug, self.name + ' request with range') success = self.write_header_with_content( socket, "206 Partial Content", header.range_start, header.range_end, self.torrent.media_file.length, self.torrent.media_file.path) if not success: return self.write_data(socket, header.range_start, header.range_end - header.range_start + 1, self.torrent.get_data) def write_header(self, socket, status): response_header = HttpHeader() response_header.status_code = status Logger().write( LogVerbosity.Info, self.name + " return header: " + response_header.to_string()) try: socket.send(response_header.to_string().encode()) return True except (ConnectionAbortedError, ConnectionResetError, OSError): Logger().write( LogVerbosity.Info, "Connection closed 2 during sending of response header") socket.close() return False def write_header_with_content(self, socket, status, start, end, length, path): response_header = HttpHeader() Logger().write( LogVerbosity.Debug, self.name + " stream requested: " + str(start) + "-" + str(end)) response_header.status_code = status response_header.content_length = end - start + 1 response_header.set_range(start, end, length) filename, file_extension = os.path.splitext(path.lower()) if file_extension not in StreamListener.mime_mapping: Logger().write( LogVerbosity.Info, self.name + " unknown video type: " + str(file_extension) + ", defaulting to mp4") response_header.mime_type = StreamListener.mime_mapping[".mp4"] else: response_header.mime_type = StreamListener.mime_mapping[ file_extension] Logger().write( LogVerbosity.Info, self.name + " return header: " + response_header.to_string()) try: socket.send(response_header.to_string().encode()) return True except (ConnectionAbortedError, ConnectionResetError, OSError): Logger().write( LogVerbosity.Info, "Connection closed 2 during sending of response header") socket.close() return False def write_data(self, socket, requested_byte, length, data_delegate): written = 0 Logger().write( LogVerbosity.Info, self.name + " write data: " + str(requested_byte) + ", length " + str(length)) id = self.id self.id += 1 data_writer = SocketWritingData(self, id, socket, requested_byte, requested_byte + length, current_time()) self.sockets_writing_data.append(data_writer) if len(self.sockets_writing_data) > 1: Logger().write(LogVerbosity.Debug, "Multiple data writers:") for writer in self.sockets_writing_data: Logger().write(LogVerbosity.Debug, " " + str(writer)) while written < length: part_length = min(length - written, self.chunk_length) if not self.running: Logger().write( LogVerbosity.Debug, self.name + ' writer ' + str(data_writer.id) + " canceling retrieving data because we are no longer running 1" ) data_writer.close() self.sockets_writing_data.remove(data_writer) return if data_writer.stop: Logger().write( LogVerbosity.Debug, self.name + ' writer ' + str(data_writer.id) + " canceling because we're seeking and expecting a new request" ) data_writer.close() self.sockets_writing_data.remove(data_writer) return if not self.wait_writable(data_writer, socket): Logger().write( LogVerbosity.Debug, self.name + ' writer ' + str(data_writer.id) + " closed") self.sockets_writing_data.remove(data_writer) return data = data_delegate(requested_byte + written, part_length) if not self.running: Logger().write( LogVerbosity.Debug, self.name + ' writer ' + str(data_writer.id) + " canceling retrieved data because we are no longer running 2" ) data_writer.close() self.sockets_writing_data.remove(data_writer) return if data is None: time.sleep(self.wait_for_data) continue Logger().write( LogVerbosity.Info, self.name + ' writer ' + str(data_writer.id) + ' data retrieved: ' + str(requested_byte + written) + " - " + str(requested_byte + written + part_length)) send = 0 try: while send < len(data): this_send = data[send:send + 50000] data_length = len(this_send) socket.sendall(this_send) written += data_length send += data_length self.bytes_send += data_length data_writer.streamed += data_length Logger().write( LogVerbosity.All, self.name + ' writer ' + str(data_writer.id) + " send " + str(data_length) + " bytes") time.sleep(0.005) # give other threads some time except (ConnectionAbortedError, ConnectionResetError, OSError) as e: Logger().write( LogVerbosity.Info, self.name + " writer " + str(data_writer.id) + " connection closed during sending of data: " + str(e)) data_writer.close() self.sockets_writing_data.remove(data_writer) return Logger().write(LogVerbosity.Info, "Completed request: " + str(data_writer)) data_writer.close() self.sockets_writing_data.remove(data_writer) def wait_writable(self, writer, socket): while True: if not self.running: return False if writer.stop: Logger().write( LogVerbosity.Debug, self.name + " canceling because we're seeking and expecting a new request" ) writer.close() return False # check if socket is still open readable, writeable, exceptional = select.select([socket], [socket], [socket], 0) if len(readable) == 1: read = [] try: read = socket.recv(1024) except Exception as e: Logger().write( LogVerbosity.Debug, "Request socket closed with exception: " + str(e)) if len(read) == 0: Logger().write(LogVerbosity.Info, self.name + " socket no longer open 3") writer.close() return False else: Logger().write( LogVerbosity.Info, self.name + " recv received data?? - " + str(read.decode("utf-8'"))) if len(writeable) == 0: # not currently writeable, wait for it to become available again time.sleep(0.1) continue return True def stop(self): self.running = False for writer in self.sockets_writing_data: writer.stop = True self.torrent = None if self.server is not None: self.server.close() Logger().write(LogVerbosity.Info, self.name + " stopped")
class App(tk.Frame): root = None @property def state(self): return self._state @state.setter def state(self, value): Logger().write( LogVerbosity.Info, "Setting UI state from " + str(self._state) + " to " + str(value)) prev_state = self._state self._state = value if value == UIState.Home: self.status_image_frame.place_forget() self.player_frame.place_forget() self.background_canvas.place(x=0, y=0) self.change_loading_visibility(False) elif value == UIState.Playing: self.status_image_frame.place_forget() self.change_loading_visibility(False) self.background_canvas.place_forget() if prev_state != UIState.Paused: self.player_frame.place(x=0, y=0) elif value == UIState.Paused: self.status_image_frame.place(x=self.parent.winfo_screenwidth() - 134, y=50) def change_loading_visibility(self, show): state = "hidden" if show: state = "normal" self.background_canvas.itemconfig(self.loading_speed_label, state=state) self.background_canvas.itemconfig(self.loading_speed_value, state=state) self.background_canvas.itemconfig(self.loading_buffered_label, state=state) self.background_canvas.itemconfig(self.loading_buffered_value, state=state) self.background_canvas.itemconfig(self.loading_peers_available_label, state=state) self.background_canvas.itemconfig(self.loading_peers_available_value, state=state) self.background_canvas.itemconfig(self.loading_peers_connected_label, state=state) self.background_canvas.itemconfig(self.loading_peers_connected_value, state=state) self.loading_details_visible = show @staticmethod def initialize(): App.root = tk.Tk() App.root.config(cursor="none") gui = App(App.root) while True: App.root.update() time.sleep(0.01) def __init__(self, parent, *args, **kwargs): tk.Frame.__init__(self, parent) self.parent = parent self._state = UIState.Home self.loading_details_visible = False self.background_time = 60 * 15 self.background_max_requests = 5 self.background_images = [] self.base_image_path = os.getcwd() + "/UI/TV/Images/" self.background_canvas = None self.background_image = None self.name_label = None self.time_label = None self.date_label = None self.playing_label = None self.playing_value = None self.info_background = None self.loading_speed_label = None self.loading_buffered_label = None self.loading_speed_value = None self.loading_buffered_value = None self.loading_peers_connected_label = None self.loading_peers_connected_value = None self.loading_peers_available_value = None self.loading_peers_available_label = None self.loading_gif = None self.rects = [] self.images = [] self.player_frame = None self.status_image_frame = None self.pause_image = None self.weather_max = None self.weather_min = None self.weather_temp = None self.weather_icon_image = None self.init_UI() VLCPlayer().player_state.register_callback(self.player_update) MediaManager().media_data.register_callback(self.media_update) MediaManager().torrent_data.register_callback(self.torrent_update) self.image_fetcher_thread = CustomThread(self.get_backgrounds, "UI background downloader") self.image_fetcher_thread.start() self.background_swapper_thread = CustomThread(self.swap_backgrounds, "UI background swapper") self.background_swapper_thread.start() self.time_change_thread = CustomThread(self.change_time, "UI time changer") self.time_change_thread.start() self.current_weather_thread = CustomThread(self.get_weather_data, "UI current weather") self.current_weather_thread.start() VLCPlayer().set_window(self.player_frame.winfo_id()) def init_UI(self): self.parent.title("TV UI") self.configure(background='black') w = self.parent.winfo_screenwidth() h = self.parent.winfo_screenheight() self.master.geometry("{0}x{1}+0+0".format(w, h)) self.parent.bind("<Escape>", self.fullscreen_cancel) self.fullscreen_toggle() self.background_canvas = tk.Canvas(self.parent, width=w, height=h, highlightthickness=0, background="#000") self.background_image = self.set_canvas_image(self.background_canvas, "background.jpg", 0, 0, w, h) self.info_background = self.create_rectangle(self.background_canvas, w - 250, 0, w, h, fill="#FFF", alpha=0.7, outline="#AAA") self.name_label = self.background_canvas.create_text( w - 125, 30, anchor="n", font=("Purisa", 20), text=Settings.get_string("name"), fill="#444") playing_position = 96 self.playing_label = self.background_canvas.create_text( w - 220, playing_position, anchor="nw", font=("Purisa", 14), text="", fill="#999") self.playing_value = self.background_canvas.create_text( w - 220, playing_position + 22, anchor="nw", font=("Purisa", 16), text="", fill="#444") self.time_label = self.background_canvas.create_text( w - 125, h - 80, font=("Purisa", 28), text=time.strftime('%H:%M'), fill="#444") self.date_label = self.background_canvas.create_text( w - 125, h - 40, font=("Purisa", 20), text=time.strftime('%a %d %b'), fill="#444") loading_position = 96 self.loading_speed_label = self.background_canvas.create_text( w - 230, loading_position, anchor="nw", font=("Purisa", 16), text="Speed:", fill="#444", state="hidden") self.loading_buffered_label = self.background_canvas.create_text( w - 230, loading_position + 24, anchor="nw", font=("Purisa", 16), text="Buffered:", fill="#444", state="hidden") self.loading_peers_connected_label = self.background_canvas.create_text( w - 230, loading_position + 48, anchor="nw", font=("Purisa", 16), text="Connected:", fill="#444", state="hidden") self.loading_peers_available_label = self.background_canvas.create_text( w - 230, loading_position + 72, anchor="nw", font=("Purisa", 16), text="Available:", fill="#444", state="hidden") self.loading_speed_value = self.background_canvas.create_text( w - 20, loading_position, anchor="ne", font=("Purisa", 16), text="", fill="#444", state="hidden") self.loading_buffered_value = self.background_canvas.create_text( w - 20, loading_position + 24, anchor="ne", font=("Purisa", 16), text="", fill="#444", state="hidden") self.loading_peers_connected_value = self.background_canvas.create_text( w - 20, loading_position + 48, anchor="ne", font=("Purisa", 16), text="", fill="#444", state="hidden") self.loading_peers_available_value = self.background_canvas.create_text( w - 20, loading_position + 72, anchor="ne", font=("Purisa", 16), text="", fill="#444", state="hidden") self.player_frame = tk.Frame(self.parent, width=w, height=h, highlightthickness=0, background="black") self.status_image_frame = tk.Canvas(self.parent, width=84, height=84, highlightthickness=0, background="#DDD") self.status_image_frame.pause_image = ImageTk.PhotoImage( Image.open(self.base_image_path + "paused.png")) self.pause_image = self.status_image_frame.create_image( 10, 10, anchor='nw', image=self.status_image_frame.pause_image) self.weather_icon_image = self.background_canvas.create_image( w - 125, h - 230, image=None) self.weather_temp = self.background_canvas.create_text(w - 125, h - 150, font=("Purisa", 20), text="", fill="#444") self.background_canvas.create_line(w - 240, 70, w - 10, 70, fill="#888") self.background_canvas.create_line(w - 240, h - 120, w - 10, h - 120, fill="#888") self.state = UIState.Home def set_canvas_image(self, canvas, name, x, y, w, h): image = Image.open(self.base_image_path + name) resized = image.resize((w, h), Image.ANTIALIAS) background_image = ImageTk.PhotoImage(resized) self.images.append(background_image) return canvas.create_image(x, y, anchor='nw', image=background_image) def change_time(self): while True: self.background_canvas.itemconfigure(self.time_label, text=time.strftime('%H:%M')) self.background_canvas.itemconfigure( self.date_label, text=time.strftime('%a %d %b')) time.sleep(0.2) def get_backgrounds(self): while True: amount = self.background_max_requests - len(self.background_images) if amount == 0: time.sleep(30) continue result = RequestFactory.make_request( "https://api.unsplash.com/photos/random/" + "?count=" + str(amount) + "&orientation=landscape" + "&collections=827743,3178572,225,573009" + "&client_id=825216e69ea20d24e5b3ddeeab316f6569dcecc4965e16a0725aee3eeb143872" ) if result is None: time.sleep(30) continue json_data = json.loads(result.decode('utf-8')) urls = [ x['urls']['raw'] + "&w=" + str(self.parent.winfo_screenwidth()) + "&h=" + str(self.parent.winfo_screenheight()) + "&fit=scale" for x in json_data ] for url in urls: image_byt = urlopen(url).read() image = Image.open(BytesIO(image_byt)) self.background_images.append(image) time.sleep(30) def create_rectangle(self, canvas, x1, y1, x2, y2, **kwargs): if 'alpha' in kwargs: alpha = int(kwargs.pop('alpha') * 255) fill = kwargs.pop('fill') fill = App.root.winfo_rgb(fill) + (alpha, ) state = 'normal' if 'state' in kwargs: state = kwargs.pop('state') image = Image.new('RGBA', (x2 - x1, y2 - y1), fill) self.rects.append(ImageTk.PhotoImage(image)) return canvas.create_image(x1, y1, image=self.rects[-1], anchor='nw', state=state) return canvas.create_rectangle(x1, y1, x2, y2, **kwargs) def swap_backgrounds(self): while True: if len(self.background_images) == 0: time.sleep(30) continue self.swap_background() time.sleep(self.background_time) def swap_background(self): img = self.background_images[0] self.background_images.remove(img) self.background_canvas.background_image = ImageTk.PhotoImage(img) self.background_canvas.itemconfig( self.background_image, image=self.background_canvas.background_image) def set_now_playing(self, title): play_label = "Now playing:" if title is None: play_label = "" self.background_canvas.itemconfigure(self.playing_label, text=play_label) self.background_canvas.itemconfigure(self.playing_value, text=title or "") def update_loading_state(self, title, speed, buffered, connected_peers, available_peers): if not self.loading_details_visible: self.background_canvas.itemconfig(self.loading_speed_label, state="normal") self.background_canvas.itemconfig(self.loading_buffered_label, state="normal") self.background_canvas.itemconfig(self.loading_speed_value, state="normal") self.background_canvas.itemconfig(self.loading_buffered_value, state="normal") self.background_canvas.itemconfig( self.loading_peers_connected_label, state="normal") self.background_canvas.itemconfig( self.loading_peers_available_label, state="normal") self.background_canvas.itemconfig( self.loading_peers_connected_value, state="normal") self.background_canvas.itemconfig( self.loading_peers_available_value, state="normal") self.loading_details_visible = True self.background_canvas.itemconfig(self.loading_speed_value, text=write_size(speed) + "ps") self.background_canvas.itemconfig(self.loading_buffered_value, text=write_size(buffered)) self.background_canvas.itemconfig(self.loading_peers_available_value, text=str(available_peers)) self.background_canvas.itemconfig(self.loading_peers_connected_value, text=str(connected_peers)) def media_update(self, old_data, new_data): if new_data.title is not None and self.state == UIState.Home: self.state = UIState.Loading if new_data.title is None and self.state != UIState.Home: self.state = UIState.Home if MediaManager().media_data.type == "Radio": self.set_now_playing(new_data.title) else: self.set_now_playing(None) def torrent_update(self, old_data, new_data): if self.state == UIState.Loading: self.update_loading_state(new_data.title, new_data.download_speed, new_data.total_streamed, new_data.connected, new_data.potential) def player_update(self, old_state, new_state): if new_state.state != old_state.state: if new_state.state == PlayerState.Playing: if MediaManager().media_data.type != "Radio": self.state = UIState.Playing else: self.state = UIState.Home if new_state.state == PlayerState.Paused: if MediaManager().media_data.type != "Radio": self.state = UIState.Paused def fullscreen_toggle(self, event="none"): self.parent.focus_set() self.parent.attributes("-fullscreen", True) self.parent.wm_attributes("-topmost", 1) def fullscreen_cancel(self, event="none"): self.parent.attributes("-fullscreen", False) self.parent.wm_attributes("-topmost", 0) self.center_window() def center_window(self): sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() w = sw * 0.7 h = sh * 0.7 x = (sw - w) / 2 y = (sh - h) / 2 self.parent.geometry("%dx%d+%d+%d" % (w, h, x, y)) def get_weather_data(self): while True: api_key = SecureSettings.get_string("open_weather_map_key") url = "http://api.openweathermap.org/data/2.5/group?id=2750947&units=metric&appid=" + api_key result = RequestFactory.make_request(url) if not result: Logger().write(LogVerbosity.Info, "Failed to get weather data") return data = json.loads(result.decode('utf8')) current_temp = data['list'][0]['main']['temp'] icon = data['list'][0]['weather'][0]['icon'].replace('n', 'd') self.background_canvas.itemconfigure( self.weather_temp, text=str(round(current_temp, 1)) + "°C") image = Image.open(self.base_image_path + "Weather/" + icon + ".png") resized = image.resize((140, 140), Image.ANTIALIAS) self.background_canvas.weather_icon = ImageTk.PhotoImage(resized) self.background_canvas.itemconfigure( self.weather_icon_image, image=self.background_canvas.weather_icon) time.sleep(60 * 30)
class Socket: def __init__(self, port, on_node_seen, on_node_timeout, on_query): self.port = port self.socket = socket(AF_INET, SOCK_DGRAM) self.socket.settimeout(0.1) self.message_thread = CustomThread(self.message_thread_action, "DHT message", []) self.running = False self.node_seen_handler = on_node_seen self.node_timeout_handler = on_node_timeout self.query_handler = on_query self.last_send = 0 self.received_messages = [] self.to_send_messages = [] self.awaiting_messages = [] def start(self): self.socket.bind(('0.0.0.0', self.port)) self.running = True self.message_thread.start() def send_response(self, msg, ip, port): self.to_send_messages.append(NodeMessage(ip, port, msg)) def send_query(self, msg, ip, port, on_response, on_timeout): self.to_send_messages.append( QueryMessage(NodeMessage(ip, port, msg), 0, on_response, on_timeout)) def message_thread_action(self): Logger().write(LogVerbosity.Debug, "Starting DHT socket") while self.running: self.receive() self.send() self.check() time.sleep(0.005) def receive(self): try: while True: data, sender = self.socket.recvfrom(2048) msg_object = BaseDHTMessage.from_bytes(data) if msg_object is None: return if isinstance(msg_object, ErrorDHTMessage): Logger().write( LogVerbosity.Debug, "DHT error message: " + str(msg_object.errorcode) + " " + str(msg_object.errormsg)) continue else: self.node_seen_handler(sender[0], sender[1], msg_object.id) msg = NodeMessage(sender[0], sender[1], msg_object) self.received_messages.append(msg) Logger().write(LogVerbosity.All, "Received DHT message") except OSError as e: return def send(self): for pending in list(self.to_send_messages): try: if not isinstance(pending, QueryMessage): data = pending.message.to_bytes() self.socket.sendto(data, (pending.ip, pending.port)) self.to_send_messages.remove(pending) Logger().write(LogVerbosity.All, "Sent DHT response") else: data = pending.message.message.to_bytes() self.socket.sendto( data, (pending.message.ip, pending.message.port)) pending.send_at = current_time() self.awaiting_messages.append(pending) self.to_send_messages.remove(pending) Logger().write(LogVerbosity.All, "Sent DHT query") except OSError as e: Logger().write(LogVerbosity.All, "Failed to send: " + str(e)) def check(self): for pending in list(self.awaiting_messages): if current_time() - pending.send_at > 10000: Logger().write(LogVerbosity.All, "DHT message timeout") self.node_timeout_handler(pending.message.ip, pending.message.port) pending.on_timeout() self.awaiting_messages.remove(pending) for received in list(self.received_messages): if isinstance(received.message, QueryDHTMessage): self.query_handler(received.ip, received.port, received.message) self.received_messages.remove(received) continue elif isinstance(received.message, ResponseDHTMessage): pending = [ x for x in self.awaiting_messages if x.message.message.transaction_id == received.message.transaction_id ] if len(pending) == 0: Logger().write(LogVerbosity.All, "DHT response for no request (timed out?)") self.received_messages.remove(received) continue Logger().write(LogVerbosity.All, "DHT message response") pending[0].on_response(received.message) # answer to request self.received_messages.remove(received) self.awaiting_messages.remove(pending[0])
class Observable(LogObject): def __init__(self, name, update_interval): super().__init__(None, name) self.__name = name self.__update_interval = update_interval self.__callbacks = [] self.__changed = True self.__last_update = 0 self.__start_state = None self.__last_update_state = None self.__wait_event = Event() self.__running = False self.__update_thread = CustomThread(self.__check_update, name + " observer") def register_callback(self, cb): self.__callbacks.append(cb) cb(self, self) def remove_callback(self, cb): self.__callbacks.remove(cb) def start_update(self): self.__start_state = { k: v for k, v in self.__dict__.items() if not k.startswith("_") } def stop_update(self): for k, v in self.__start_state.items(): if self.__dict__[k] != v: self.__changed = True self.__wait_event.set() break if not self.__running: self.__update_thread.start() def changed(self): self.__changed = True self.__wait_event.set() if not self.__running: self.__update_thread.start() def reset(self): self.start_update() for k, v in self.__start_state.items(): if isinstance(v, int) or isinstance(v, float): self.__dict__[k] = 0 else: self.__dict__[k] = None self.stop_update() def __check_update(self): self.__running = True while self.__running: self.__wait_event.wait(self.__update_interval) self.__wait_event.clear() if current_time() - self.__last_update < (self.__update_interval * 1000): continue if self.__changed: self.__changed = False self.__last_update = current_time() dic = { k: v for k, v in self.__dict__.items() if not k.startswith("_") } last_update = namedtuple("Observable", dic.keys())(*dic.values()) for cb in self.__callbacks: try: cb(self.__last_update_state or self, self) except Exception as e: Logger().write_error(e, "Exception in observer callback") self.__last_update_state = last_update
class Peer(TorrentManager): @property def bitfield(self): # Only create a new bitfield when we actually have metadata for it if self.__bitfield is None and not self.torrent.is_preparing: self.__bitfield = Bitfield(self, self.torrent.data_manager.total_pieces) return self.__bitfield @property def state(self): return self._state @state.setter def state(self, value): if self._state != value: old = self._state self._state = value if self.torrent.peer_manager is not None: self.torrent.peer_manager.update_peer(self, old, value) if value == PeerState.Started: self.peer_state_task = CustomThread( self.check_peer_state, "Check peer state " + str(self.id)) self.peer_state_task.start() def __init__(self, id, torrent, uri, source): super().__init__(torrent, "Peer " + str(id)) self.id = id self.torrent = torrent self.uri = uri self._state = PeerState.Initial self.source = source self.connection_manager = None self.download_manager = None self.metadata_manager = None self.extension_manager = None self.__bitfield = None self.communication_state = PeerCommunicationState(self) self.counter = None self.peer_speed = PeerSpeed.Low self.allowed_fast_pieces = [] self.round_trips = 0 self.round_trip_total = 0 self.round_trip_average = 0 self.max_blocks_log = 0 self.speed_log = 0 self.peer_start_task = None self.peer_state_task = None self.peer_stop_task = None self.stop_reason = "Torrent stopping" self.protocol_logger = ProtocolLogger(self) def adjust_round_trip_time(self, time): self.round_trips += 1 self.round_trip_total += time self.round_trip_average = self.round_trip_total / self.round_trips def start(self): Logger().write(LogVerbosity.All, str(self.id) + ' Starting peer') self.state = PeerState.Starting self.connection_manager = PeerConnectionManager(self, self.uri) self.download_manager = PeerDownloadManager(self) self.metadata_manager = PeerMetaDataManager(self) self.extension_manager = PeerExtensionManager(self) self.counter = AverageCounter(self, 3) self.peer_start_task = CustomThread(self.connection_manager.start, "Start peer " + str(self.id)) self.peer_start_task.start() Logger().write(LogVerbosity.Debug, str(self.id) + ' Peer started') def update(self): self.metadata_manager.update() self.download_manager.update() def check_peer_state(self): zero_speed_checks = 0 while self.state == PeerState.Started: if current_time() - self.connection_manager.connected_on < 20000: time.sleep(1) continue if not self.torrent.peer_manager.should_stop_peers(): time.sleep(1) continue if self.counter.total == 0: Logger().write(LogVerbosity.Info, str(self.id) + " stopping not downloading peer") self.stop_async( "Not downloading" ) # Stop since we haven't downloaded anything since connecting return if self.counter.value == 0: zero_speed_checks += 1 if zero_speed_checks >= 10: Logger().write( LogVerbosity.Info, str(self.id) + " stopping currently not downloading peer") self.stop_async( "Not recently downloading" ) # Stop since we haven't downloaded anything in the last 10 seconds return else: zero_speed_checks = 0 time.sleep(1) @staticmethod def add_connected_peer_stat(source): if source == PeerSource.DHT: Stats().add('peers_source_dht_connected', 1) elif source == PeerSource.HttpTracker: Stats().add('peers_source_http_tracker_connected', 1) elif source == PeerSource.UdpTracker: Stats().add('peers_source_udp_tracker_connected', 1) elif source == PeerSource.PeerExchange: Stats().add('peers_source_exchange_connected', 1) def stop_async(self, reason): if self.state != PeerState.Started and self.state != PeerState.Starting: return self.stop_reason = reason self.state = PeerState.Stopping self.peer_stop_task = CustomThread(self.stop, "Peer stopper " + str(self.id), []) self.peer_stop_task.start() def stop(self): if self.state == PeerState.Stopped: return self.state = PeerState.Stopping Logger().write(LogVerbosity.All, str(self.id) + ' Peer stopping') if self.peer_state_task is not None: self.peer_state_task.join() if self.state == PeerState.Stopped: return self.connection_manager.disconnect() self.download_manager.stop() self.extension_manager.stop() self.metadata_manager.stop() self.state = PeerState.Stopped self.peer_start_task.join() self.protocol_logger = None super().stop() self.finish() Logger().write(LogVerbosity.Debug, str(self.id) + ' Peer stopped: ' + self.stop_reason)
def stop_torrent(self): if self.torrent: thread = CustomThread(self.torrent.stop, "Torrent stopper") thread.start() self.torrent = None
class VLCPlayer(metaclass=Singleton): def __init__(self): self.__vlc_instance = None self.player_state = PlayerData() self.instantiate_vlc() self.media = None self.__player = self.__vlc_instance.media_player_new() self.__list_player = self.__vlc_instance.media_list_player_new() self.__list_player.set_media_player(self.__player) self.__event_manager = self.__player.event_manager() self.set_volume(75) EventManager.register_event(EventType.SetSubtitleFiles, self.set_subtitle_files) EventManager.register_event(EventType.StopPlayer, self.stop) self.player_observer = CustomThread(self.observe_player, "Player observer") self.player_observer.start() self.stop_player_thread = None def instantiate_vlc(self): parameters = self.get_instance_parameters() Logger().write(LogVerbosity.Debug, "VLC parameters: " + str(parameters)) self.__vlc_instance = vlc.Instance("cvlc", *parameters) Logger().write(LogVerbosity.Info, "VLC version " + libvlc_get_version().decode('utf8')) def play(self, url, time=0): parameters = self.get_play_parameters(url, time) Logger().write(LogVerbosity.Info, "VLC Play | Url: " + url) Logger().write(LogVerbosity.Info, "VLC Play | Time: " + str(time)) Logger().write(LogVerbosity.Info, "VLC Play | Parameters: " + str(parameters)) self.player_state.start_update() self.player_state.path = url self.player_state.stop_update() self.media = Media(url, *parameters) if 'youtube' in url: media_list = MediaList() media_list.add_media(self.media) self.__list_player.set_media_list(media_list) self.__list_player.play() else: self.__player.set_media(self.media) self.__player.play() @staticmethod def get_instance_parameters(): params = [ "--verbose=" + str(Settings.get_int("vlc_log_level")), "--network-caching=" + str(Settings.get_int("network_caching")), "--ipv4-timeout=500", "--image-duration=-1" ] if sys.platform == "linux" or sys.platform == "linux2": log_path = Settings.get_string( "base_folder") + "/Logs/" + datetime.datetime.now().strftime( '%Y-%m-%d %H-%M-%S') params.append( "--logfile=" + log_path + '/vlc_' + datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') + ".txt") params.append("--file-logging") params.append("--file-caching=5000") return params def get_play_parameters(self, url, time): params = [] if time != 0: params.append("start-time=" + str(time // 1000)) return params def set_window(self, handle): if sys.platform == "linux" or sys.platform == "linux2": self.__player.set_xwindow(handle) else: self.__player.set_hwnd(handle) def pause_resume(self): Logger().write(LogVerbosity.All, "Player pause resume") self.__player.pause() def stop(self): Logger().write(LogVerbosity.All, "Player stop") thread = CustomThread(lambda: self.__player.stop(), "Stopping VLC player") thread.start() def set_volume(self, vol): Logger().write(LogVerbosity.Debug, "Player set volume " + str(vol)) self.__player.audio_set_volume(vol) self.player_state.start_update() self.player_state.volume = vol self.player_state.stop_update() def get_volume(self): return self.__player.audio_get_volume() def get_position(self): return self.__player.get_time() def get_length(self): return int(self.__player.get_length()) def set_time(self, pos): Logger().write(LogVerbosity.Debug, "Player set time " + str(pos)) self.__player.set_time(pos) self.player_state.start_update() self.player_state.playing_for = pos self.player_state.stop_update() def set_position(self, pos): Logger().write(LogVerbosity.Debug, "Player set position " + str(pos)) self.__player.set_position(pos) def set_subtitle_delay(self, delay): Logger().write(LogVerbosity.Debug, "Player set subtitle delay " + str(delay)) self.__player.video_set_spu_delay(delay) self.player_state.start_update() self.player_state.sub_delay = delay self.player_state.stop_update() def get_state(self): return self.__player.get_state() def get_audio_track(self): return self.__player.audio_get_track() def set_audio_track(self, track_id): Logger().write(LogVerbosity.Debug, "Player set audio track " + str(track_id)) self.__player.audio_set_track(track_id) self.player_state.start_update() self.player_state.audio_track = track_id self.player_state.stop_update() def get_audio_tracks(self): tracks = self.__player.audio_get_track_description() result = [] for trackid, trackname in tracks: result.append((trackid, trackname.decode('utf8'))) return result def set_subtitle_files(self, files): Logger().write(LogVerbosity.Debug, "Adding " + str(len(files)) + " subtitle files") pi = sys.platform == "linux" or sys.platform == "linux2" for file in reversed(files): if not pi and file[1] != ":": file = "C:" + file file = file.replace("/", os.sep).replace("\\", os.sep) # NOTE this must be called after Play() self.__player.video_set_subtitle_file(file) def set_subtitle_track(self, id): Logger().write(LogVerbosity.Debug, "Player set subtitle track " + str(id)) self.__player.video_set_spu(id) self.player_state.start_update() self.player_state.sub_track = id self.player_state.stop_update() def get_subtitle_count(self): return self.__player.video_get_spu_count() def get_subtitle_tracks(self): tracks = self.__player.video_get_spu_description() result = [] for trackid, trackname in tracks: result.append((trackid, trackname.decode('utf-8'))) return result def get_subtitle_delay(self): return self.__player.video_get_spu_delay() def get_selected_sub(self): return self.__player.video_get_spu() def try_play_subitem(self): media = self.__player.get_media() if media is None: self.stop() return subs = media.subitems() if subs is None: self.stop() return if len(subs) == 1: subs[0].add_options("demux=avformat") self.__player.set_media(subs[0]) self.__player.play() def observe_player(self): while True: state = self.get_state().value if state in [5, 6, 7]: state = 0 new_state = PlayerState(state) if new_state == PlayerState.Nothing and self.player_state.state != PlayerState.Nothing: self.stop_player_thread = CustomThread(self.stop, "Stopping player") self.stop_player_thread.start() self.player_state.start_update() self.player_state.state = new_state self.player_state.playing_for = self.get_position() self.player_state.length = self.get_length() self.player_state.audio_tracks = self.get_audio_tracks() self.player_state.audio_track = self.get_audio_track() self.player_state.sub_delay = self.get_subtitle_delay() self.player_state.sub_track = self.get_selected_sub() self.player_state.sub_tracks = self.get_subtitle_tracks() self.player_state.volume = self.get_volume() self.player_state.stop_update() time.sleep(0.5)
class PeerConnectionManager(LogObject): @property def ready_for_sending(self): return len(self.to_send_bytes) > 0 and current_time() - self.last_send > 10 @property def ready_for_reading(self): return len(self.buffer) >= self._next_message_length def __init__(self, peer, uri): super().__init__(peer, "connection") self.peer = peer self.uri = uri self.to_send_bytes = bytearray() self.last_send = 0 self.connected_on = 0 self._last_communication = 0 self._peer_timeout = Settings.get_int("peer_timeout") self._connection_timeout = Settings.get_int("connection_timeout") / 1000 self.connection = TcpClient(uri.hostname, uri.port, self._connection_timeout) self.buffer = bytearray() self.buffer_in_size = 0 self.buffer_out_size = 0 self._next_message_length = 1 self._buffer_position = 0 self._receive_state = ReceiveState.ReceiveLength self._receive_buffer_size = 32768 self.in_thread = None self.reading_handshake = True def start(self): self.connected_on = 0 Logger().write(LogVerbosity.All, str(self.peer.id) + ' connecting to ' + str(self.uri.netloc)) Stats().add('peers_connect_try', 1) if not self.connection.connect(): Stats().add('peers_connect_failed', 1) if self.peer is not None: Logger().write(LogVerbosity.All, str(self.peer.id) + ' could not connect to ' + str(self.uri.netloc)) self.peer.stop_async("Can't connect") return if self.peer.state is not PeerState.Starting: return self.connected_on = current_time() self.peer.add_connected_peer_stat(self.peer.source) Stats().add('peers_connect_success', 1) Logger().write(LogVerbosity.Debug, str(self.peer.id) + ' connected to ' + str(self.uri.netloc)) self.peer.state = PeerState.Started self.in_thread = CustomThread(self.socket_reader, "Peer " + str(self.peer.id) + " input") self.in_thread.start() def socket_reader(self): while self.peer.state == PeerState.Started: if len(self.buffer) > 1000000: time.sleep(0.01) continue data = self.connection.receive_available(self._receive_buffer_size) if data is None or len(data) == 0: self.peer.stop_async("Reading error") break self._last_communication = current_time() self.buffer += data self.buffer_in_size = len(self.buffer) time.sleep(0.001) def handle_read(self): received_messages = [] messages_size = 0 while True: buffer_size = len(self.buffer) if buffer_size - self._buffer_position < self._next_message_length: # cant read another complete message break # form messages from the buffer: if self._receive_state == ReceiveState.ReceiveLength: if self.reading_handshake: offset, id_length = read_byte_as_int(self.buffer, 0) msg_length = id_length + 49 # Handshake sends the length of the id, the rest of the handshake takes 49 bytes if msg_length > len(self.buffer): break self.reading_handshake = False else: offset, msg_length = read_integer(self.buffer, self._buffer_position) self._buffer_position += 4 self._next_message_length = msg_length self._receive_state = ReceiveState.ReceiveMessage if self._next_message_length < 0 or self._next_message_length > 17000: Logger().write(LogVerbosity.Info, "Invalid next message length: " + str(self._next_message_length)) self.peer.stop_async("Invalid next msg length") break else: total_data = self._buffer_position + self._next_message_length message = bytes(self.buffer[self._buffer_position: total_data]) self._buffer_position = total_data self._next_message_length = 4 self._receive_state = ReceiveState.ReceiveLength messages_size += len(message) received_messages.append(message) self.buffer = self.buffer[self._buffer_position:] self._buffer_position = 0 self.buffer_in_size = len(self.buffer) return messages_size, received_messages def handle_write(self): success = self.connection.send(self.to_send_bytes) self.to_send_bytes.clear() self.buffer_out_size = 0 self.last_send = current_time() self._last_communication = current_time() if not success: self.peer.stop_async("Write error") def send(self, data): self.to_send_bytes.extend(data) self.buffer_out_size = len(self.to_send_bytes) def disconnect(self): if self.connected_on != 0: Logger().write(LogVerbosity.Debug, str(self.peer.id) + ' disconnected') self.connection.disconnect() self.to_send_bytes.clear() if self.in_thread is not None: self.in_thread.join() self.buffer.clear() self.peer = None
def request_master_cb(topic, callback, timeout, *args): data = to_JSON(args) request = SlaveClientController._send_request(topic, data) thread = CustomThread(SlaveClientController.wait_for_request_response, "Request callback " + topic, [request, timeout, callback]) thread.start()
class TradfriManager(metaclass=Singleton): api_factory = None api = None gateway = None enabled = False initialized = False last_init = 0 def __init__(self): self.tradfri_state = TradfriState() self.observing_end = 0 self.observing = False self.observe_thread = None def init(self): if self.initialized: Logger().write(LogVerbosity.Info, "already init") return if sys.platform != "linux" and sys.platform != "linux2": Logger().write(LogVerbosity.Info, "Lighting: Not initializing, no coap client available on windows") self.initialized = True self.tradfri_state.update_group(DeviceGroup(1, "Test group", True, 128, 6)) self.tradfri_state.update_group(DeviceGroup(2, "Test group 2", False, 18, 6)) return if current_time() - self.last_init < 5000: Logger().write(LogVerbosity.Info, "Tried initialization less than 5s ago") return Logger().write(LogVerbosity.All, "Start LightManager init") self.enabled = True if not self.initialized: ip = Settings.get_string("tradfri_hub_ip") identity = Database().get_stat_string("LightingId") key = Database().get_stat_string("LightingKey") if identity is None or key is None: Logger().write(LogVerbosity.Info, "Lighting: No identity/key found, going to generate new") # We don't have all information to connect, reset and start from scratch Database().remove_stat("LightingId") Database().remove_stat("LightingKey") key = None identity = uuid.uuid4().hex Database().update_stat("LightingId", identity) # Generate and save a new id self.api_factory = APIFactory(host=ip, psk_id=identity) else: self.api_factory = APIFactory(host=ip, psk_id=identity, psk=key) self.api = self.api_factory.request self.gateway = Gateway() if key is None: try: security_code = SecureSettings.get_string("tradfri_hub_code") # the code at the bottom of the hub key = self.api_factory.generate_psk(security_code) Database().update_stat("LightingKey", key) # Save the new key Logger().write(LogVerbosity.Info, "Lighting: New key retrieved") except Exception as e: Logger().write_error(e, "Unhandled exception") return else: Logger().write(LogVerbosity.Info, "Lighting: Previously saved key found") try: self.initialized = True groups = self.get_device_groups() for group in groups: self.tradfri_state.update_group(group) except Exception as e: Logger().write(LogVerbosity.Info, "Failed to init tradfri, clearing previous key to try generate new") self.initialized = False Database().remove_stat("LightingId") Database().remove_stat("LightingKey") Logger().write_error(e, "Failed to get groups from hub") def start_observing(self): Logger().write(LogVerbosity.Debug, "Start observing light data") self.observing = True if self.observing_end > current_time(): Logger().write(LogVerbosity.All, "Still observing, not starting again") return # still observing, the check observing thread will renew if not self.check_state(): return self.observing_end = current_time() + 30000 groups_commands = self.api(self.gateway.get_groups()) result = self.api(groups_commands) for group in result: self.observe_group(group) def observe_group(self, group): Logger().write(LogVerbosity.All, "Starting observe for group " + group.name) self.observe_thread = CustomThread(lambda: self.api(group.observe( self.tradfri_state.update_group, lambda x: self.check_observe(group), duration=30)), "Light group observer", []) self.observe_thread.start() def check_observe(self, group): if self.observing: # Restart observing since it timed out Logger().write(LogVerbosity.Debug, "Restarting observing for group " + str(group.name)) self.observe_group(group) def stop_observing(self): Logger().write(LogVerbosity.Debug, "Stop observing light data") self.observing = False def get_devices(self): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get devices") devices_commands = self.api(self.gateway.get_devices()) devices = self.api(devices_commands) return [d for d in [self.parse_device(x) for x in devices] if d is not None] def set_state(self, device_id, state): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set device state") device = self.api(self.gateway.get_device(device_id)) if device.has_light_control: self.api(device.light_control.set_state(state)) else: self.api(device.socket_control.set_state(state)) def set_light_warmth(self, device_id, warmth): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set light warmth") device = self.api(self.gateway.get_device(device_id)) self.api(device.light_control.set_color_temp(warmth)) def set_light_dimmer(self, device_id, amount): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set light dimmer") device = self.api(self.gateway.get_device(device_id)) self.api(device.light_control.set_dimmer(amount)) def set_device_name(self, device_id, name): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set device name") device = self.api(self.gateway.get_device(device_id)) self.api(device.set_name(name)) def get_device_groups(self): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get device groups") groups_commands = self.api(self.gateway.get_groups()) result = self.api(groups_commands) Logger().write(LogVerbosity.All, "Get device groups: " + str([x.raw for x in result])) return [DeviceGroup(group.id, group.name, group.state, group.dimmer, len(group.member_ids)) for group in result] def get_devices_in_group(self, group): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get lights in group") group = self.api(self.gateway.get_group(group)) members = group.member_ids result = [self.api(self.gateway.get_device(x)) for x in members] return [d for d in [self.parse_device(x) for x in result] if d is not None] def set_group_name(self, group, name): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group name") group = self.api(self.gateway.get_group(group)) self.api(group.set_name(name)) def set_group_state(self, group, state): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group state") group = self.api(self.gateway.get_group(group)) self.api(group.set_state(state)) def set_group_dimmer(self, group, dimmer): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group dimmer") group = self.api(self.gateway.get_group(group)) self.api(group.set_dimmer(dimmer)) def check_state(self): if not self.enabled: return False # not available if not self.initialized: self.init() if not self.initialized: return False # init failed return True @staticmethod def parse_device(data): if data.has_light_control: lights = [] for light in data.light_control.lights: lights.append(LightDevice( light.state, light.dimmer, light.color_temp, light.hex_color)) return LightControl(data.id, data.name, data.application_type, data.last_seen.timestamp(), data.reachable, data.light_control.can_set_dimmer, data.light_control.can_set_temp, data.light_control.can_set_color, lights) elif data.has_socket_control: sockets = [] for socket in data.socket_control.sockets: sockets.append(SocketDevice(socket.state)) return SocketControl(data.id, data.name, data.application_type, data.last_seen.timestamp(), data.reachable, sockets)
def throw_event(event_type, args): thread = CustomThread(EventManager.execute_handlers, "EventHandler " + str(event_type), args=[event_type, args]) thread.start()
class MediaManager(metaclass=Singleton): def __init__(self): self.media_data = MediaData() self.torrent_data = TorrentData() self.torrent = None self.subtitle_provider = SubtitleProvider() self.next_episode_manager = NextEpisodeManager() self.play_position = 0 self.play_length = 0 self.history_id = 0 self.last_tracking_update = 0 self.dht_enabled = Settings.get_bool("dht") if self.dht_enabled: self.dht = DHTEngine() self.dht.start() EventManager.register_event(EventType.AbortingTorrent, self.aborting_torrent) EventManager.register_event(EventType.TorrentMediaSelectionRequired, self.media_selection_required) EventManager.register_event(EventType.TorrentMediaFileSet, lambda x: self._start_playing_torrent()) EventManager.register_event(EventType.TorrentStopped, lambda: self.on_torrent_stopped()) VLCPlayer().player_state.register_callback(self.player_state_change) self.torrent_observer = CustomThread(self.observe_torrent, "Torrent observer") self.torrent_observer.start() self.next_epi_thread = None def start_file(self, url, time): actual_url = url if Settings.get_bool("slave"): actual_url = "http://" + Settings.get_string( "master_ip") + ":50015/file/" + urllib.parse.quote(url) self.stop_play() VLCPlayer().play(actual_url, time) if Settings.get_bool("slave"): self.history_id, = SlaveClientController.request_master( "add_watched_file", 5, url, current_time()) else: self.history_id = Database().add_watched_file(url, current_time()) self.media_data.start_update() self.media_data.type = "File" self.media_data.title = os.path.basename(url) self.media_data.url = url self.media_data.image = None self.media_data.stop_update() TVManager().switch_input_to_pi() def start_radio(self, name, url): self.stop_play() VLCPlayer().play(url, 0) self.media_data.start_update() self.media_data.type = "Radio" self.media_data.title = name self.media_data.image = None self.media_data.stop_update() TVManager().switch_input_to_pi() def start_episode(self, id, season, episode, title, url, image, position): self.stop_play() self._start_torrent(url, None) self.media_data.start_update() self.media_data.type = "Show" self.media_data.title = title self.media_data.image = image self.media_data.season = season self.media_data.id = id self.media_data.episode = episode self.media_data.start_from = position self.media_data.stop_update() def start_torrent(self, title, url, media_file=None): self.stop_play() self._start_torrent(url, media_file) self.media_data.start_update() self.media_data.type = "Torrent" self.media_data.title = title self.media_data.image = None self.media_data.stop_update() def start_movie(self, id, title, url, image, position): self.stop_play() self._start_torrent(url, None) self.media_data.start_update() self.media_data.type = "Movie" self.media_data.title = title self.media_data.image = image self.media_data.id = id self.media_data.start_from = position self.media_data.stop_update() def start_youtube(self, title, url, position): self.stop_play() VLCPlayer().play(url, position) if Settings.get_bool("slave"): self.history_id, = SlaveClientController.request_master( "add_watched_youtube", 5, title, url, current_time()) else: self.history_id = Database().add_watched_youtube( title, url, current_time()) self.media_data.start_update() self.media_data.type = "YouTube" self.media_data.url = url self.media_data.title = title self.media_data.stop_update() TVManager().switch_input_to_pi() def start_url(self, title, url): self.stop_play() VLCPlayer().play(url, 0) if Settings.get_bool("slave"): self.history_id, = SlaveClientController.request_master( "add_watched_url", 5, url, current_time()) else: self.history_id = Database().add_watched_url(url, current_time()) self.media_data.start_update() self.media_data.type = "Url" self.media_data.title = title self.media_data.stop_update() TVManager().switch_input_to_pi() def pause_resume(self): VLCPlayer().pause_resume() def seek(self, position): VLCPlayer().set_time(position) def change_subtitle(self, track): VLCPlayer().set_subtitle_track(track) def change_audio(self, track): VLCPlayer().set_audio_track(track) def change_volume(self, change_volume): VLCPlayer().set_volume(change_volume) def change_subtitle_delay(self, delay): VLCPlayer().set_subtitle_delay(delay) def stop_play(self): stop_torrent = False if VLCPlayer().player_state.state == PlayerState.Nothing: stop_torrent = True VLCPlayer().stop() if stop_torrent: self.stop_torrent() while self.torrent is not None: time.sleep(0.2) self.media_data.reset() def aborting_torrent(self, reason): APIController().ui_message("Aborting torrent", reason) self.stop_play() @staticmethod def on_torrent_stopped(): time.sleep(2) gc.collect() time.sleep(1) obj = objgraph.by_type('MediaPlayer.Torrents.Torrent.Torrent.Torrent') if len(obj) != 0: Logger().write(LogVerbosity.Important, "Torrent not disposed!") else: Logger().write(LogVerbosity.Info, "Torrent disposed") def play_next_episode(self, continue_next): if continue_next: if self.next_episode_manager.next_type == "File": self.start_file(self.next_episode_manager.next_path, 0) elif self.next_episode_manager.next_type == "Show": self.start_episode(self.next_episode_manager.next_id, self.next_episode_manager.next_season, self.next_episode_manager.next_episode, self.next_episode_manager.next_title, self.next_episode_manager.next_path, self.next_episode_manager.next_img, 0) else: self.start_torrent(self.next_episode_manager.next_title, self.next_episode_manager.next_path, self.next_episode_manager.next_media_file) Logger().write( LogVerbosity.Info, "Playing next: " + self.next_episode_manager.next_title) self.next_episode_manager.reset() def media_selection_required(self, files): if Settings.get_bool("slave"): data, = SlaveClientController.request_master( "get_history_for_url", 5, self.torrent.uri) if data: history = [ History(x['id'], x['imdb_id'], x['type'], x['title'], x['image'], x['watched_at'], x['season'], x['episode'], x['url'], x['media_file'], x['played_for'], x['length']) for x in json.loads(data) ] else: history = [] else: history = Database().get_history_for_url(self.torrent.uri) for file in files: seen = [x for x in history if x.media_file == file.path] file.seen = len(seen) > 0 if file.seen: seen = seen[-1] file.played_for = seen.played_for file.play_length = seen.length APIController().ui_request("SelectMediaFile", self.set_media_file, 60 * 30, files) def set_media_file(self, file, position): if not file: self.stop_play() else: self.media_data.start_from = position self.torrent.set_media_file(file) def _start_playing_torrent(self): if Settings.get_bool("slave"): self.history_id, = SlaveClientController.request_master( "add_watched_torrent", 5, self.media_data.type, self.media_data.title, self.media_data.id, self.torrent.uri, self.torrent.media_file.path, self.media_data.image, self.media_data.season, self.media_data.episode, current_time()) else: self.history_id = Database().add_watched_torrent( self.media_data.type, self.media_data.title, self.media_data.id, self.torrent.uri, self.torrent.media_file.path, self.media_data.image, self.media_data.season, self.media_data.episode, current_time()) VLCPlayer().play("http://localhost:50009/torrent", self.media_data.start_from) def _start_torrent(self, url, media_file): if self.torrent is not None: Logger().write(LogVerbosity.Important, "Can't start new torrent, still torrent active") return success, torrent = Torrent.create_torrent(1, url) if success: self.torrent = torrent if media_file is not None: self.torrent.set_selected_media_file(media_file) torrent.start() self.last_torrent_start = current_time() TVManager().switch_input_to_pi() else: Logger().write(LogVerbosity.Important, "Invalid torrent") EventManager.throw_event(EventType.Error, ["torrent_error", "Invalid torrent"]) self.stop_torrent() def player_state_change(self, old_state, new_state): if old_state.state != new_state.state: Logger().write( LogVerbosity.Info, "Player state changed from " + str(old_state.state) + " to " + str(new_state.state)) if new_state.state == PlayerState.Playing: self.play_position = new_state.playing_for self.play_length = new_state.length if old_state.state != PlayerState.Paused and old_state.state != new_state.state and new_state.state == PlayerState.Playing: self.update_subtitles(new_state) self.next_epi_thread = CustomThread( lambda: self.next_episode_manager.check_next_episode( self.media_data, self.torrent), "Check next episode", []) self.next_epi_thread.start() if old_state.state != new_state.state and new_state.state == PlayerState.Nothing: self.history_id = 0 self.media_data.reset() self.stop_torrent() if self.play_length != 0 and self.play_position / self.play_length > 0.9: self.next_episode_manager.notify_next_episode( self.play_next_episode) else: self.next_episode_manager.reset() self.play_position = 0 self.play_length = 0 self.update_tracking(new_state) def update_subtitles(self, new_state): media_type = self.media_data.type if media_type == "File": if Settings.get_bool("slave"): SlaveClientController.request_master_cb( "get_file_info", self.process_file_info_for_subtitles, 5, self.media_data.url) else: size, first_64k, last_64k = get_file_info(self.media_data.url) EventManager.throw_event(EventType.SearchSubtitles, [ self.media_data.title, size, VLCPlayer().get_length(), first_64k, last_64k ]) elif media_type == "Show" or media_type == "Movie" or media_type == "Torrent": EventManager.throw_event(EventType.SearchSubtitles, [ os.path.basename(self.torrent.media_file.name), self.torrent.media_file.length, VLCPlayer().get_length(), self.torrent.media_file.first_64k, self.torrent.media_file.last_64k ]) def process_file_info_for_subtitles(self, size, first_64k, last_64k): Logger().write(LogVerbosity.Debug, "Received file info from master, requesting subs") EventManager.throw_event(EventType.SearchSubtitles, [ self.media_data.title, size, VLCPlayer().get_length(), first_64k.encode('utf8'), last_64k.encode('utf8') ]) def update_tracking(self, state): if self.media_data.type == "Radio": return if self.history_id == 0 or state.state != PlayerState.Playing or current_time( ) - self.last_tracking_update < 5000: return if state.playing_for > state.length - ( state.length * 0.04) or state.length - state.playing_for < 10000: if Settings.get_bool("slave"): SlaveClientController.notify_master("update_watching_item", self.history_id, state.length, state.length, current_time()) else: Database().update_watching_item(self.history_id, state.length, state.length, current_time()) else: if Settings.get_bool("slave"): SlaveClientController.notify_master("update_watching_item", self.history_id, state.playing_for, state.length, current_time()) else: Database().update_watching_item(self.history_id, state.playing_for, state.length, current_time()) self.last_tracking_update = current_time() def stop_torrent(self): if self.torrent: thread = CustomThread(self.torrent.stop, "Torrent stopper") thread.start() self.torrent = None def observe_torrent(self): while True: if self.torrent is None or self.torrent.state == TorrentState.Stopping: self.torrent_data.reset() time.sleep(0.5) continue self.torrent_data.start_update() self.torrent_data.title = self.torrent.name self.torrent_data.size = self.torrent.total_size if self.torrent.media_file is not None: self.torrent_data.media_file = self.torrent.media_file.name self.torrent_data.size = self.torrent.media_file.length self.torrent_data.downloaded = self.torrent.network_manager.average_download_counter.total self.torrent_data.left = self.torrent.left self.torrent_data.overhead = self.torrent.overhead self.torrent_data.download_speed = self.torrent.network_manager.average_download_counter.value self.torrent_data.buffer_position = self.torrent.stream_buffer_position self.torrent_data.buffer_total = self.torrent.bytes_total_in_buffer self.torrent_data.stream_position = self.torrent.stream_position self.torrent_data.buffer_size = self.torrent.bytes_ready_in_buffer self.torrent_data.total_streamed = self.torrent.bytes_streamed self.torrent_data.state = self.torrent.state self.torrent_data.potential = len( self.torrent.peer_manager.potential_peers) self.torrent_data.connecting = len( self.torrent.peer_manager.connecting_peers) self.torrent_data.connected = len( self.torrent.peer_manager.connected_peers) self.torrent_data.disconnected = len( self.torrent.peer_manager.disconnected_peers) self.torrent_data.cant_connect = len( self.torrent.peer_manager.cant_connect_peers) self.torrent_data.stop_update() time.sleep(0.5)
def request_cb(self, topic, callback, timeout, room, *args): request_message = self._send_request(topic, args, room) thread = CustomThread(self.wait_for_request_response, "Request callback " + topic, [request_message, timeout, callback]) thread.start()
class Engine(LogObject): def __init__(self, name, tick_time=1000, parent=None): super().__init__(parent, "Engine:" + name) self.name = name self.tick_time = tick_time self.last_tick = 0 self.running = False self.thread = None self.work_items = [] self.current_item = None self.start_time = 0 self.timing = dict() self.own_time = TimingObject(self.name) # Log props self.current_item_log = "" def runner(self): while self.running: sleep(0.01) if not self.running: break self.last_tick = current_time() self.tick() def start(self): self.running = True self.thread = CustomThread(self.runner, self.name) self.thread.start() def add_work_item(self, name, interval, work_item, initial_invoke=True): self.work_items.append(EngineWorkItem(self, name, interval, work_item, initial_invoke)) def stop(self): self.running = False self.thread.join() def tick(self): tick_time = current_time() cur_list = list(self.work_items) for i in range(len(cur_list)): work_item = cur_list[i] if work_item.last_run_time + work_item.interval < tick_time: Timing().start_timing("Engine item " + work_item.name) self.current_item = work_item self.current_item_log = work_item.name self.start_time = current_time() if not self.running: return result = work_item.action() self.current_item = None self.current_item_log = "" test_time = current_time() work_item.last_run_time = test_time work_item.last_run_duration = test_time - self.start_time if work_item.interval == -1: self.work_items.remove(work_item) elif not result: self.work_items.remove(work_item) if work_item.name not in self.timing: self.timing[work_item.name] = TimingObject(work_item.name) self.timing[work_item.name].add_time(current_time() - self.start_time) Timing().stop_timing("Engine item " + work_item.name) sleep(0) self.own_time.add_time(current_time() - tick_time)
class RuleManager(metaclass=Singleton): conditions = { 1: IsBetweenTimeCondition, 2: IsPassingTimeCondition, 3: IsHomeCondition, 4: OnLeavingHomeCondition, 5: OnComingHomeCondition } actions = { 1: ToggleTradfriGroupAction, 2: SetTemperatureAction, 3: ToggleTvAction, 4: PlayRadioAction } def __init__(self): self.running = False self.current_rules = [] self.check_thread = CustomThread(self.check_rules, "Rule checker") self.load_rules() enabled = Database().get_stat("rules_enabled") self.enabled = bool(enabled) def start(self): if Settings.get_bool("slave"): return self.running = True self.check_thread.start() def stop(self): self.running = False self.check_thread.join() def set_enabled(self, enabled): self.enabled = enabled Database().update_stat("rules_enabled", enabled) def check_rules(self): while self.running: Logger().write(LogVerbosity.All, "Checking rules") for rule in self.current_rules: if rule.check(): Logger().write( LogVerbosity.Info, "Executing rule " + rule.name + ": " + rule.description) if self.enabled: try: rule.execute() except Exception as e: Logger().write_error(e, "Rule error") Database().update_rule(rule) time.sleep(10) def update_rule(self, rule_id, active, name, actions, conditions): if rule_id == -1: rule = Rule(-1, name, current_time(), True, 0) self.current_rules.append(rule) else: rule = [x for x in self.current_rules if x.id == rule_id][0] rule.name = name rule.active = active rule.actions = [] for action in actions: rule.add_action(-1, action[0], [x for x in action[1:] if x is not None]) rule.conditions = [] for condition in conditions: rule.add_condition(-1, condition[0], [x for x in condition[1:] if x is not None]) rule.last_execution = 0 Database().save_rule(rule) def remove_rule(self, rule_id): self.current_rules = [x for x in self.current_rules if x.id != rule_id] Database().remove_rule(rule_id) def get_rule(self, rule_id): return [x for x in self.current_rules if x.id == rule_id][0] def get_rules(self): return self.enabled, self.current_rules def get_actions_and_conditions(self): actions = [ ActionModel(id, action.name, action.description, action.parameter_descriptions) for id, action in self.actions.items() ] conditions = [ ActionModel(id, condition.name, condition.description, condition.parameter_descriptions) for id, condition in self.conditions.items() ] return actions, conditions def load_rules(self): db_rules = Database().get_rules() self.current_rules = self.parse_rules(db_rules) def parse_rules(self, rules): result = [] for r in rules: rule = Rule(r.id, r.name, r.created, r.active, r.last_execution) for link in r.links: if link.rule_link_type == "Condition": rule.add_condition(link.id, link.link_type, link.parameters) else: rule.add_action(link.id, link.link_type, link.parameters) result.append(rule) return result @staticmethod def update_check_time(hour, minute): result = datetime.now() if result.hour > hour or (result.hour == hour and result.minute >= minute): result += timedelta(days=1) result = result.replace(hour=hour, minute=minute, second=0, microsecond=0) return result