def set_media_file(self, path): if "x265" in path.lower(): self.abort("HVEC x265 files not supported") return self.media_file = [x for x in self.files if x.path == path][0] Logger().write( LogVerbosity.Info, "Media file: " + str(self.media_file.name) + ", " + str(self.media_file.start_byte) + " - " + str(self.media_file.end_byte) + "/" + str(self.total_size)) self.data_manager.set_piece_info(self.piece_length, self.piece_hashes) self.cache_manager.init(self.piece_length, self.media_file.length, self.media_file.start_byte) self.__set_state(TorrentState.Downloading) self.left = self.data_manager.get_piece_by_offset( self.media_file.end_byte ).end_byte - self.data_manager.get_piece_by_offset( self.media_file.start_byte).start_byte Logger().write( LogVerbosity.Info, "To download: " + str(self.left) + " (" + str(self.left - self.media_file.length) + " overhead), piece length: " + str(self.piece_length)) EventManager.throw_event(EventType.TorrentMediaFileSet, [self.media_file.name])
def announce_torrent(self, torrent): self.last_announce = current_time() announce_message = TrackerMessages.TrackerAnnounceMessage.for_http(torrent.info_hash, 2, torrent.total_size - torrent.left, torrent.left, torrent.uploaded, self.tracker_peer_request_amount) path = self.uri.path + announce_message.as_param_string() response = RequestFactory.make_request(path) if response is None: return False try: response_dict = Bencode.bdecode(response) except BTFailure: Logger().write(LogVerbosity.Info, 'Invalid tracker response: ' + str(response)) return False if b"peers" not in response_dict: return False peers_data = response_dict[b"peers"] total_peers = int(len(peers_data) / 6) offset = 0 peers = [] for index in range(total_peers): peers.append(uri_from_bytes(peers_data[offset:offset + 6])) offset += 6 EventManager.throw_event(EventType.PeersFound, [peers, PeerSource.HttpTracker])
def stop(self): if self.__state == TorrentState.Stopping: return Logger().write(LogVerbosity.Info, 'Torrent stopping') self.__set_state(TorrentState.Stopping) EventManager.deregister_event(self._user_file_selected_id) self.engine.stop() Logger().write(LogVerbosity.Debug, 'Torrent engines stopped') self.peer_processor.stop() self.peer_manager.stop() self.tracker_manager.stop() self.network_manager.stop() self.metadata_manager.stop() self.stream_manager.stop() self.message_processor.stop() self.download_manager.stop() self.data_manager.stop() self.cache_manager.stop() Logger().write(LogVerbosity.Debug, 'Torrent managers stopped') for file in self.files: file.close() self.files = [] self.media_file = None self.finish() EventManager.throw_event(EventType.TorrentStopped, []) Logger().write(LogVerbosity.Important, 'Torrent stopped')
def __init__(self, torrent): super().__init__(torrent, "download") self.torrent = torrent self.init = False self.prio = False self.last_get_result = 0, 0 self.last_get_time = 0 self.queue = [] self.slow_peer_piece_offset = 0 self._ticks = 0 self.start_piece = 0 self.end_piece = 0 self.stream_end_buffer_pieces = 0 self.stream_play_buffer_high_priority = 0 self.max_chunk_size = Settings.get_int("max_chunk_size") self.download_mode = DownloadMode.Full self.peers_per_piece = [(100, 2, 5), (95, 1, 2), (0, 1, 1)] self._event_id_stopped = EventManager.register_event( EventType.TorrentStopped, self.unregister) self._event_id_torrent_state = EventManager.register_event( EventType.TorrentStateChange, self.init_queue) self.queue_log = ""
def __init__(self): self.subtitle_sources = [ SubtitlesOpenSubtitles(), ] self.sub_file_directory = Settings.get_string("base_folder") + "/subs/" self.sub_files = [] self.file_size = 0 self.file_length = 0 self.file_name = None self.first_64k = None self.last_64k = None # create subtitles directory if not os.path.exists(self.sub_file_directory): os.makedirs(self.sub_file_directory) # remove old subtitles files file_list = glob.glob(self.sub_file_directory + "*.srt") for f in file_list: os.remove(f) EventManager.register_event(EventType.SearchSubtitles, self.search_subtitles)
def wait_for_event(max_time, event): UtilController.health_cache[event] = False evnt = EventManager.register_event( event, lambda *x: UtilController.assign(event)) result = UtilController.wait_for( max_time, lambda: UtilController.health_cache[event]) EventManager.deregister_event(evnt) return result
def __set_state(self, value): Logger().write( LogVerbosity.Info, "Setting torrent state from " + TorrentState.get_str(self.__state) + " to " + TorrentState.get_str(value)) old = self.__state self.__state = value EventManager.throw_event(EventType.TorrentStateChange, [old, value])
def request_movies(url): data = RequestFactory.make_request(url) if data is not None: return MovieController.parse_movie_data(data.decode('utf-8')) else: EventManager.throw_event(EventType.Error, ["get_error", "Could not get movie data"]) Logger().write(LogVerbosity.Info, "Error fetching movies") return []
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 search_subtitles_thread(self, source, size, file_length, file_name, first_64k, last_64k): sub_paths = source.get_subtitles(size, file_length, file_name, first_64k, last_64k) for path in sub_paths: file_hash = self.get_sub_hash(path) if len([x for x in self.sub_files if x.hash == file_hash]) == 0: self.sub_files.append(Subtitle(file_hash, path)) Logger().write(LogVerbosity.Info, "Found " + str(len(sub_paths)) + " subtitle files") EventManager.throw_event(EventType.SetSubtitleFiles, [sub_paths])
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 __init__(self): self.own_node = None self.routing_table = Table(self) self.table_lock = Lock() self.socket = Socket(Settings.get_int("dht_port"), self.on_node_seen, self.on_node_timeout, self.on_query) self.engine = Engine("DHT Engine", 5000) self.engine.add_work_item("DHT Refresh buckets", 1000 * 60, self.refresh_buckets, False) self.engine.add_work_item("DHT Save nodes", 1000 * 60 * 5, self.save_nodes, False) self.running_tasks = [] self.torrent_nodes = dict() EventManager.register_event(EventType.RequestPeers, self.search_peers) EventManager.register_event(EventType.NewDHTNode, self.add_node)
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 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 search_peers(self, torrent): if torrent.info_hash.sha1_hashed_bytes in self.torrent_nodes: Logger().write( LogVerbosity.Debug, "DHT: found " + str( len(self.torrent_nodes[ torrent.info_hash.sha1_hashed_bytes])) + " nodes in torrent_nodes") EventManager.throw_event(EventType.PeersFound, [[ x.uri for x in self.torrent_nodes[ torrent.info_hash.sha1_hashed_bytes] ], PeerSource.DHT]) self.start_task( GetPeersTask( self, self.own_node.byte_id, torrent.info_hash.sha1_hashed_bytes, self.routing_table.get_closest_nodes( torrent.info_hash.sha1_hashed_bytes)), lambda x: EventManager.throw_event( EventType.PeersFound, [x.found_peers, PeerSource.DHT]))
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 announce_torrent(self, torrent): self.last_announce = current_time() if not self.connect(): return False announce_message = TrackerMessages.TrackerAnnounceMessage.for_udp(self.connection_id, self.transaction_id, torrent.info_hash, 2, torrent.total_size - torrent.left, torrent.left, torrent.uploaded, self.tracker_peer_request_amount, 6881) send_okay = self.connection.send(announce_message.as_bytes()) response_message_bytes = self.connection.receive() if not send_okay or response_message_bytes is None: return False response_message = TrackerMessages.TrackerResponseMessage.from_bytes(response_message_bytes) if response_message is None or response_message.error is not None: return False EventManager.throw_event(EventType.PeersFound, [response_message.peers, PeerSource.UdpTracker]) return True
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 __init__(self, torrent): super().__init__(torrent, "peers") self.torrent = torrent self.potential_peers = [] self.connecting_peers = [] self.connected_peers = [] self.disconnected_peers = [] self.cant_connect_peers = [] self.complete_peer_list = [] self.max_peers_connected = Settings.get_int("max_peers_connected") self.max_peers_connecting = Settings.get_int("max_peers_connecting") self._peer_request_interval = Settings.get_int("peer_request_interval") self._peer_request_interval_no_potential = Settings.get_int( "peer_request_interval_no_potential") self.random = Random() self.download_start = 0 self.start_time = current_time() self.last_peer_request = 0 self.checked_no_peers = False self._event_id_stopped = EventManager.register_event( EventType.TorrentStopped, self.unregister) self._event_id_torrent_change = EventManager.register_event( EventType.TorrentStateChange, self.torrent_state_change) self._event_id_peers_found = EventManager.register_event( EventType.PeersFound, self.add_potential_peers) self.high_speed_peers = 0 self.medium_speed_peers = 0 # Log properties self.potential_peers_log = 0 self.connecting_peers_log = 0 self.connected_peers_log = 0 self.disconnected_peers_log = 0 self.cant_connect_peers_log = 0
def __init__(self, torrent): super().__init__(torrent, "data") self.torrent = torrent self._pieces = dict() self.init_done = False self.total_pieces = 0 self.piece_length = 0 self.bitfield = None self.hashes = [] self.block_size = Settings.get_int("block_size") self.broadcasted_hash_data = False self._event_id_stopped = EventManager.register_event( EventType.TorrentStopped, self.unregister) self.blocks_done_length = 0 self.last_piece_index = 0
def __init__(self, id, uri): super().__init__(None, "Torrent") self.left = 0 self.overhead = 0 self.name = None self.id = id self.uri = uri self.total_size = 0 self.uploaded = 0 self.piece_length = 0 self.piece_hashes = None self.announce_uris = [] self.info_hash = None self.files = [] self.__state = TorrentState.Initial self.media_file = None self.selected_media_file = None self.stream_file_hash = None self._user_file_selected_id = EventManager.register_event( EventType.TorrentMediaFileSelection, self.user_file_selected) self.engine = Engine.Engine('Main processor', 500, self) self.tracker_manager = TrackerManager() self.peer_manager = TorrentPeerManager(self) self.data_manager = TorrentDataManager(self) self.download_manager = TorrentDownloadManager(self) self.stream_manager = StreamManager(self) self.metadata_manager = TorrentMetadataManager(self) self.network_manager = TorrentNetworkManager(self) self.message_processor = TorrentMessageProcessor(self) self.peer_processor = TorrentPeerProcessor(self) self.cache_manager = TorrentCacheManager(self)
def __init__(self): self.trackers = [] self.initialized = False self.request_peers_id = EventManager.register_event(EventType.RequestPeers, self.request_peers)
def unregister(self): EventManager.deregister_event(self._event_id_stopped) EventManager.deregister_event(self._event_id_torrent_change) EventManager.deregister_event(self._event_id_peers_found)
def stop(self): EventManager.deregister_event(self.request_peers_id)
def handle_message(self, peer, message, timestamp): if peer.protocol_logger is None: return if isinstance(message, PieceMessage): Logger().write( LogVerbosity.All, str(peer.id) + ' Received piece message: ' + str(message.index) + ', offset ' + str(message.offset)) peer.protocol_logger.update("Sending/receiving requests", True) self.torrent.data_manager.block_done(peer, message.index, message.offset, message.data) peer.download_manager.block_done( message.index * self.torrent.data_manager.piece_length + message.offset, timestamp) peer.counter.add_value(message.length) return elif isinstance(message, KeepAliveMessage): Logger().write(LogVerbosity.Debug, str(peer.id) + ' Received keep alive message') peer.protocol_logger.update("Received KeepAlive") return elif isinstance(message, ChokeMessage): Logger().write(LogVerbosity.Debug, str(peer.id) + ' Received choke message') peer.protocol_logger.update("Received Choke") peer.communication_state.in_choke = PeerChokeState.Choked return elif isinstance(message, UnchokeMessage): Logger().write(LogVerbosity.Debug, str(peer.id) + ' Received unchoke message') peer.protocol_logger.update("Received UnChoke") peer.communication_state.in_choke = PeerChokeState.Unchoked return elif isinstance(message, InterestedMessage): Logger().write(LogVerbosity.Info, str(peer.id) + ' Received interested message') peer.protocol_logger.update("Received Interested") peer.communication_state.in_interest = PeerInterestedState.Interested return elif isinstance(message, UninterestedMessage): Logger().write(LogVerbosity.Debug, str(peer.id) + ' Received uninterested message') peer.protocol_logger.update("Received Uninterested") peer.communication_state.in_interest = PeerInterestedState.Uninterested return elif isinstance(message, HaveMessage): if peer.state == PeerState.Started: Logger().write( LogVerbosity.All, str(peer.id) + ' Received have message for piece ' + str(message.piece_index)) peer.protocol_logger.update("Received Have", True) peer.bitfield.update_piece(message.piece_index, True) return elif isinstance(message, BitfieldMessage): if peer.state == PeerState.Started: Logger().write(LogVerbosity.All, str(peer.id) + ' Received bitfield message') peer.protocol_logger.update("Received Bitfield") peer.bitfield.update(message.bitfield) return elif isinstance(message, RequestMessage): Logger().write(LogVerbosity.Info, str(peer.id) + ' Received request message') peer.protocol_logger.update("Received Request") return elif isinstance(message, CancelMessage): Logger().write(LogVerbosity.Debug, str(peer.id) + ' Received cancel message') peer.protocol_logger.update("Received Cancel") return elif isinstance(message, PortMessage): Logger().write( LogVerbosity.All, str(peer.id) + ' Received port message, port = ' + str(message.port)) peer.protocol_logger.update("Received Port") EventManager.throw_event( EventType.NewDHTNode, [peer.connection_manager.uri.hostname, message.port]) return elif isinstance(message, HaveAllMessage): if peer.state == PeerState.Started: Logger().write(LogVerbosity.All, str(peer.id) + " Received HaveAll message") peer.protocol_logger.update("Received HaveAll") peer.bitfield.set_has_all() return elif isinstance(message, HaveNoneMessage): if peer.state == PeerState.Started: Logger().write(LogVerbosity.All, str(peer.id) + " Received HaveNone message") peer.protocol_logger.update("Received HaveNone") peer.bitfield.set_has_none() return elif isinstance(message, AllowedFastMessage): if peer.state == PeerState.Started: Logger().write(LogVerbosity.All, str(peer.id) + " Received AllowedFast message") peer.protocol_logger.update("Received AllowedFast", True) peer.allowed_fast_pieces.append(message.piece_index) return elif isinstance(message, SuggestPieceMessage): Logger().write(LogVerbosity.All, str(peer.id) + " Received SuggestPiece message") peer.protocol_logger.update("Received SuggestPiece", True) return elif isinstance(message, RejectRequestMessage): if peer.state == PeerState.Started: Logger().write( LogVerbosity.Debug, str(peer.id) + " Received RejectRequest message") peer.protocol_logger.update("Received RejectRequest", True) peer.download_manager.request_rejected(message.index, message.offset, message.data_length) return elif isinstance(message, ExtensionHandshakeMessage): Logger().write( LogVerbosity.All, str(peer.id) + ' Received extension handshake message') peer.protocol_logger.update("Received ExtensionHandshake") try: dic = Bencode.bdecode(message.bencoded_payload) except BTFailure: Logger().write(LogVerbosity.Debug, "Invalid extension handshake received") peer.stop_async("Invalid extension handshake") return peer.extension_manager.parse_dictionary(dic) if b'metadata_size' in dic: if peer is not None: self.torrent.metadata_manager.set_total_size( dic[b'metadata_size']) return elif isinstance(message, PeerExchangeMessage): peer.protocol_logger.update("Received PeerExchange") Logger().write( LogVerbosity.Debug, str(peer.id) + ' Received ' + str(len(message.added)) + ' peers from peer exchange') self.torrent.peer_manager.add_potential_peers( message.added, PeerSource.PeerExchange) return elif isinstance(message, MetadataMessage): if message.metadata_message_type == MetadataMessageType.Data: peer.protocol_logger.update("Received Metadata") Logger().write( LogVerbosity.Debug, str(peer.id) + ' Received metadata message index ' + str(message.piece_index)) self.torrent.metadata_manager.add_metadata_piece( message.piece_index, message.data) else: peer.protocol_logger.update("Received Metadata rejected") Logger().write( LogVerbosity.Debug, str(peer.id) + ' Received metadata reject message ' + str(message.piece_index)) return
def abort(self, reason): Logger().write(LogVerbosity.Important, "Aborting torrent: " + reason) EventManager.throw_event(EventType.AbortingTorrent, [reason])
def unregister(self): EventManager.deregister_event(self._event_id_stopped)
def parse_info_dictionary(self, info_dict): self.name = info_dict[b'name'].decode('utf8') if b'files' in info_dict: # Multifile files = info_dict[b'files'] total_length = 0 for file in files: file_length = file[b'length'] path = self.name path_list = file[b'path'] last_path = "" for path_part in path_list: path += "/" + path_part.decode('utf8') last_path = path_part.decode('utf8') fi = TorrentDownloadFile(self, file_length, total_length, last_path, path, is_media_file(path)) self.files.append(fi) total_length += file_length Logger().write(LogVerbosity.Info, "File: " + fi.path) else: # Singlefile total_length = info_dict[b'length'] file = TorrentDownloadFile(self, total_length, 0, self.name, self.name, is_media_file(self.name)) self.files.append(file) Logger().write(LogVerbosity.Info, "File: " + file.path) self.piece_length = info_dict[b'piece length'] self.piece_hashes = info_dict[b'pieces'] self.total_size = total_length media_files = [x for x in self.files if x.is_media] if len(media_files) == 0: # No media file, can't play so just stop Logger().write(LogVerbosity.Important, "No media file found in torrent, stopping") EventManager.throw_event(EventType.StopPlayer, []) elif len(media_files) == 1: # Single media file, just play that self.set_media_file(media_files[0].path) elif self.selected_media_file is not None: self.set_media_file([ x for x in media_files if x.name == self.selected_media_file ][0].path) else: ordered = sorted(media_files, key=lambda x: x.length, reverse=True) biggest = ordered[0] second = ordered[1] if biggest.length > second.length * 8: # One file is significantly bigger than the others, play that self.set_media_file(biggest.path) else: # Multiple files, let user decide self.__set_state(TorrentState.WaitingUserFileSelection) for file in media_files: season, epi = try_parse_season_episode(file.path) if season: file.season = season if epi: file.episode = epi EventManager.throw_event( EventType.TorrentMediaSelectionRequired, [media_files]) Logger().write(LogVerbosity.Info, "Torrent metadata read")
def update_new_peers(self): if self.torrent.state != TorrentState.Downloading and self.torrent.state != TorrentState.DownloadingMetaData and self.torrent.state != TorrentState.WaitingUserFileSelection: return True if current_time() - self.last_peer_request > self._peer_request_interval or \ len(self.potential_peers) <= 10 and current_time() - self.last_peer_request > self._peer_request_interval_no_potential: Logger().write(LogVerbosity.Debug, "Requesting peers") EventManager.throw_event(EventType.RequestPeers, [self.torrent]) self.last_peer_request = current_time() currently_connecting = len(self.connecting_peers) currently_connected = len(self.connected_peers) if not self.checked_no_peers and current_time( ) - self.start_time > 45000: self.checked_no_peers = True if len( self.potential_peers ) == 0 and currently_connecting == 0 and currently_connected == 0: self.torrent.abort("No peers available") return False if currently_connected >= self.max_peers_connected: # already have max connections return True if currently_connecting >= self.max_peers_connecting: # already have max connecting return True connected_peers_under_max = self.max_peers_connected - currently_connected connecting_peers_under_max = self.max_peers_connecting - currently_connecting peers_to_connect = min(connecting_peers_under_max, connected_peers_under_max) peer_list = self.potential_peers # Try connecting to new peers from potential list peer_list_size = len(peer_list) using_disconnected = False if peer_list_size == 0: using_disconnected = True peer_list = [ x for x in self.disconnected_peers if current_time() - x[2] > 10000 ][peers_to_connect:] # If we dont have any new peers to try, try connecting to disconnected peers peer_list_size = len(peer_list) if peer_list_size == 0: return True # No peers available peers_to_connect = min(peer_list_size, peers_to_connect) Logger().write(LogVerbosity.Debug, 'starting ' + str(peers_to_connect) + ' new peers') selected_peers = self.random.sample(peer_list, peers_to_connect) if not using_disconnected: self.potential_peers = [ x for x in peer_list if x not in selected_peers ] else: self.disconnected_peers = [ x for x in peer_list if x not in selected_peers ] for peer in selected_peers: self.__peer_id += 1 new_peer = Peer(self.__peer_id, self.torrent, peer[0], peer[1]) self.connecting_peers.append(new_peer) new_peer.start() self.potential_peers_log = len(self.potential_peers) self.disconnected_peers_log = len(self.disconnected_peers) return True