From c7f2a861d5002f40250801a6701bc6ebc3207eda Mon Sep 17 00:00:00 2001 From: Jonh Alex <122692601+Jonhvmp@users.noreply.github.com> Date: Sat, 5 Oct 2024 02:54:56 -0300 Subject: [PATCH] Implement error handling improvements in main.py and torrent_downloader.py - Added robust try-except blocks for all critical functions in both files. - Improved initialization error handling for torrent session in torrent_downloader.py. - Enhanced error logging for better debugging and clearer feedback when exceptions occur. - Refined torrent action functions (start, pause, cancel, abort) to handle failures gracefully. - Updated get_download_status to manage missing torrent handles and report issues accurately. Contributed by: Jonhvmp --- torrent-client/main.py | 195 ++++++++++++++++----------- torrent-client/torrent_downloader.py | 109 +++++++++------ 2 files changed, 186 insertions(+), 118 deletions(-) diff --git a/torrent-client/main.py b/torrent-client/main.py index 7fbc49d8..f8d7e5b6 100644 --- a/torrent-client/main.py +++ b/torrent-client/main.py @@ -13,10 +13,13 @@ start_download_payload = sys.argv[4] torrent_downloader = None -if start_download_payload: - initial_download = json.loads(urllib.parse.unquote(start_download_payload)) - torrent_downloader = TorrentDownloader(torrent_port) - torrent_downloader.start_download(initial_download['game_id'], initial_download['magnet'], initial_download['save_path']) +try: + if start_download_payload: + initial_download = json.loads(urllib.parse.unquote(start_download_payload)) + torrent_downloader = TorrentDownloader(torrent_port) + torrent_downloader.start_download(initial_download['game_id'], initial_download['magnet'], initial_download['save_path']) +except (json.JSONDecodeError, KeyError, ValueError) as e: + sys.stderr.write(f"Failed to start torrent download: {e}\n") class Handler(BaseHTTPRequestHandler): rpc_password_header = 'x-hydra-rpc-password' @@ -30,95 +33,131 @@ class Handler(BaseHTTPRequestHandler): sys.stderr.write("%s - - [%s] %s\n" % (self.address_string(), self.log_date_time_string(), - format%args)) + format % args)) def log_message(self, format, *args): for route in self.skip_log_routes: - if route in args[0]: return - + if route in args[0]: + return super().log_message(format, *args) - + def do_GET(self): - if self.path == "/status": - if self.headers.get(self.rpc_password_header) != rpc_password: - self.send_response(401) - self.end_headers() - return + try: + if self.path == "/status": + if self.headers.get(self.rpc_password_header) != rpc_password: + self.send_response(401) + self.end_headers() + return - self.send_response(200) - self.send_header("Content-type", "application/json") - self.end_headers() - - status = torrent_downloader.get_download_status() - - self.wfile.write(json.dumps(status).encode('utf-8')) - - elif self.path == "/healthcheck": - self.send_response(200) - self.end_headers() - - elif self.path == "/process-list": - if self.headers.get(self.rpc_password_header) != rpc_password: - self.send_response(401) - self.end_headers() - return - - process_list = [proc.info for proc in psutil.process_iter(['exe', 'pid', 'username'])] - - self.send_response(200) - self.send_header("Content-type", "application/json") - self.end_headers() - - self.wfile.write(json.dumps(process_list).encode('utf-8')) - - def do_POST(self): - global torrent_downloader - - if self.headers.get(self.rpc_password_header) != rpc_password: - self.send_response(401) - self.end_headers() - return - - content_length = int(self.headers['Content-Length']) - post_data = self.rfile.read(content_length) - data = json.loads(post_data.decode('utf-8')) - - if self.path == "/profile-image": - parsed_image_path = data['image_path'] - - try: - parsed_image_path, mime_type = ProfileImageProcessor.process_image(parsed_image_path) self.send_response(200) self.send_header("Content-type", "application/json") self.end_headers() - - self.wfile.write(json.dumps({'imagePath': parsed_image_path, 'mimeType': mime_type}).encode('utf-8')) - except: - self.send_response(400) + + status = torrent_downloader.get_download_status() + self.wfile.write(json.dumps(status).encode('utf-8')) + + elif self.path == "/healthcheck": + self.send_response(200) self.end_headers() - elif self.path == "/action": - if torrent_downloader is None: - torrent_downloader = TorrentDownloader(torrent_port) + elif self.path == "/process-list": + if self.headers.get(self.rpc_password_header) != rpc_password: + self.send_response(401) + self.end_headers() + return - if data['action'] == 'start': - torrent_downloader.start_download(data['game_id'], data['magnet'], data['save_path']) - elif data['action'] == 'pause': - torrent_downloader.pause_download(data['game_id']) - elif data['action'] == 'cancel': - torrent_downloader.cancel_download(data['game_id']) - elif data['action'] == 'kill-torrent': - torrent_downloader.abort_session() - torrent_downloader = None + process_list = [proc.info for proc in psutil.process_iter(['exe', 'pid', 'username'])] - self.send_response(200) + self.send_response(200) + self.send_header("Content-type", "application/json") + self.end_headers() + + self.wfile.write(json.dumps(process_list).encode('utf-8')) + except Exception as e: + sys.stderr.write(f"Error in GET request: {e}\n") + self.send_response(500) self.end_headers() - else: - self.send_response(404) - self.end_headers() + def do_POST(self): + global torrent_downloader + try: + if self.headers.get(self.rpc_password_header) != rpc_password: + self.send_response(401) + self.end_headers() + return + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + data = json.loads(post_data.decode('utf-8')) + + if self.path == "/profile-image": + parsed_image_path = data['image_path'] + try: + parsed_image_path, mime_type = ProfileImageProcessor.process_image(parsed_image_path) + self.send_response(200) + self.send_header("Content-type", "application/json") + self.end_headers() + self.wfile.write(json.dumps({'imagePath': parsed_image_path, 'mimeType': mime_type}).encode('utf-8')) + except Exception as e: + sys.stderr.write(f"Error processing profile image: {e}\n") + self.send_response(400) + self.end_headers() + + elif self.path == "/action": + if torrent_downloader is None: + torrent_downloader = TorrentDownloader(torrent_port) + + if data['action'] == 'start': + try: + torrent_downloader.start_download(data['game_id'], data['magnet'], data['save_path']) + except Exception as e: + sys.stderr.write(f"Error starting torrent: {e}\n") + self.send_response(500) + self.end_headers() + return + + elif data['action'] == 'pause': + try: + torrent_downloader.pause_download(data['game_id']) + except Exception as e: + sys.stderr.write(f"Error pausing torrent: {e}\n") + self.send_response(500) + self.end_headers() + return + + elif data['action'] == 'cancel': + try: + torrent_downloader.cancel_download(data['game_id']) + except Exception as e: + sys.stderr.write(f"Error canceling torrent: {e}\n") + self.send_response(500) + self.end_headers() + return + + elif data['action'] == 'kill-torrent': + try: + torrent_downloader.abort_session() + torrent_downloader = None + except Exception as e: + sys.stderr.write(f"Error killing torrent: {e}\n") + self.send_response(500) + self.end_headers() + return + + self.send_response(200) + self.end_headers() + else: + self.send_response(404) + self.end_headers() + except Exception as e: + sys.stderr.write(f"Error in POST request: {e}\n") + self.send_response(500) + self.end_headers() if __name__ == "__main__": - httpd = HTTPServer(("", int(http_port)), Handler) - httpd.serve_forever() + try: + httpd = HTTPServer(("", int(http_port)), Handler) + sys.stderr.write(f"Server running on port {http_port}\n") + httpd.serve_forever() + except Exception as e: + sys.stderr.write(f"Failed to start HTTP server: {e}\n") diff --git a/torrent-client/torrent_downloader.py b/torrent-client/torrent_downloader.py index d59cd28b..f6ee0382 100644 --- a/torrent-client/torrent_downloader.py +++ b/torrent-client/torrent_downloader.py @@ -4,7 +4,11 @@ class TorrentDownloader: def __init__(self, port: str): self.torrent_handles = {} self.downloading_game_id = -1 - self.session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=port)}) + try: + self.session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=port)}) + except Exception as e: + raise RuntimeError(f"Failed to initialize torrent session: {e}") + self.trackers = [ "udp://tracker.opentrackr.org:1337/announce", "http://tracker.opentrackr.org:1337/announce", @@ -103,56 +107,81 @@ class TorrentDownloader: ] def start_download(self, game_id: int, magnet: str, save_path: str): - params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers} - torrent_handle = self.session.add_torrent(params) - self.torrent_handles[game_id] = torrent_handle - torrent_handle.set_flags(lt.torrent_flags.auto_managed) - torrent_handle.resume() + try: + params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers} + torrent_handle = self.session.add_torrent(params) + self.torrent_handles[game_id] = torrent_handle + torrent_handle.set_flags(lt.torrent_flags.auto_managed) + torrent_handle.resume() + + self.downloading_game_id = game_id + except Exception as e: + raise RuntimeError(f"Failed to start download for game {game_id}: {e}") + - self.downloading_game_id = game_id def pause_download(self, game_id: int): - torrent_handle = self.torrent_handles.get(game_id) - if torrent_handle: - torrent_handle.pause() - torrent_handle.unset_flags(lt.torrent_flags.auto_managed) - self.downloading_game_id = -1 + try: + torrent_handle = self.torrent_handles.get(game_id) + if torrent_handle: + torrent_handle.pause() + torrent_handle.unset_flags(lt.torrent_flags.auto_managed) + self.downloading_game_id = -1 + else: + raise KeyError(f"Torrent handle not found for game {game_id}") + except Exception as e: + raise RuntimeError(f"Failed to pause download for game {game_id}: {e}") + def cancel_download(self, game_id: int): - torrent_handle = self.torrent_handles.get(game_id) - if torrent_handle: - torrent_handle.pause() - self.session.remove_torrent(torrent_handle) - self.torrent_handles[game_id] = None - self.downloading_game_id = -1 + try: + torrent_handle = self.torrent_handles.get(game_id) + if torrent_handle: + torrent_handle.pause() + self.session.remove_torrent(torrent_handle) + self.torrent_handles[game_id] = None + self.downloading_game_id = -1 + else: + raise KeyError(f"Torrent handle not found for game {game_id}") + except Exception as e: + raise RuntimeError(f"Failed to cancel download for game {game_id}: {e}") + def abort_session(self): - for game_id in self.torrent_handles: - torrent_handle = self.torrent_handles[game_id] - torrent_handle.pause() - self.session.remove_torrent(torrent_handle) - - self.session.abort() - self.torrent_handles = {} - self.downloading_game_id = -1 + try: + for game_id in self.torrent_handles: + torrent_handle = self.torrent_handles[game_id] + torrent_handle.pause() + self.session.remove_torrent(torrent_handle) + + self.session.abort() + self.torrent_handles = {} + self.downloading_game_id = -1 + except Exception as e: + raise RuntimeError(f"Failed to abort torrent session: {e}") def get_download_status(self): if self.downloading_game_id == -1: return None - torrent_handle = self.torrent_handles.get(self.downloading_game_id) + try: + torrent_handle = self.torrent_handles.get(self.downloading_game_id) + if not torrent_handle: + raise KeyError(f"Torrent handle not found for game {self.downloading_game_id}") - status = torrent_handle.status() - info = torrent_handle.get_torrent_info() + status = torrent_handle.status() + info = torrent_handle.get_torrent_info() - return { - 'folderName': info.name() if info else "", - 'fileSize': info.total_size() if info else 0, - 'gameId': self.downloading_game_id, - 'progress': status.progress, - 'downloadSpeed': status.download_rate, - 'numPeers': status.num_peers, - 'numSeeds': status.num_seeds, - 'status': status.state, - 'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download, - } + return { + 'folderName': info.name() if info else "", + 'fileSize': info.total_size() if info else 0, + 'gameId': self.downloading_game_id, + 'progress': status.progress, + 'downloadSpeed': status.download_rate, + 'numPeers': status.num_peers, + 'numSeeds': status.num_seeds, + 'status': status.state, + 'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download, + } + except Exception as e: + raise RuntimeError(f"Failed to get download status for game {self.downloading_game_id}: {e}")