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
This commit is contained in:
Jonh Alex 2024-10-05 02:54:56 -03:00
parent 37111c11d8
commit c7f2a861d5
2 changed files with 186 additions and 118 deletions

View File

@ -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")

View File

@ -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}")