This commit is contained in:
N0\A
2025-10-24 19:03:04 +02:00
parent b9e2da327a
commit a0417d7267
2 changed files with 7 additions and 158 deletions

View File

@@ -46,7 +46,6 @@ class DuktoProtocol:
self.on_send_complete: Optional[Callable[[List[str]], None]] = None self.on_send_complete: Optional[Callable[[List[str]], None]] = None
self.on_transfer_progress: Optional[Callable[[int, int], None]] = None self.on_transfer_progress: Optional[Callable[[int, int], None]] = None
self.on_error: Optional[Callable[[str], None]] = None self.on_error: Optional[Callable[[str], None]] = None
self.on_incoming_connection: Optional[Callable[[str, socket.socket], None]] = None
def set_ports(self, udp_port: int, tcp_port: int): def set_ports(self, udp_port: int, tcp_port: int):
self.local_udp_port = udp_port self.local_udp_port = udp_port
@@ -193,22 +192,12 @@ class DuktoProtocol:
conn.close() conn.close()
continue continue
if self.on_incoming_connection: threading.Thread(target=self._receive_files,
# Pass the connection to the handler to decide args=(conn, addr[0]), daemon=True).start()
self.on_incoming_connection(addr[0], conn)
else:
# Auto-reject if no handler is set
conn.close()
except Exception as e: except Exception as e:
if self.running: if self.running:
print(f"TCP listener error: {e}") print(f"TCP listener error: {e}")
def start_receiving(self, conn: socket.socket, sender_ip: str):
"""Starts the receiving process on an already accepted connection."""
threading.Thread(target=self._receive_files,
args=(conn, sender_ip), daemon=True).start()
def _receive_files(self, conn: socket.socket, sender_ip: str): def _receive_files(self, conn: socket.socket, sender_ip: str):
self.is_receiving = True self.is_receiving = True

150
main.py
View File

@@ -1,5 +1,5 @@
#!/usr/bin/python3 #!/usr/bin/python3
import sys, os, subprocess, socket import sys, os, subprocess
from pathlib import Path from pathlib import Path
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
from pynput import keyboard from pynput import keyboard
@@ -9,7 +9,6 @@ from core.web_search import MullvadLetaWrapper
from core.discord_presence import presence from core.discord_presence import presence
from core.app_launcher import list_apps, launch from core.app_launcher import list_apps, launch
from core.updater import update_repository, is_update_available from core.updater import update_repository, is_update_available
from core.dukto import DuktoProtocol, Peer
ASSET = Path(__file__).parent / "assets" / "2ktan.png" ASSET = Path(__file__).parent / "assets" / "2ktan.png"
@@ -319,14 +318,6 @@ class WebSearchResults(QtWidgets.QDialog):
class MainWindow(QtWidgets.QMainWindow): class MainWindow(QtWidgets.QMainWindow):
show_menu_signal = QtCore.Signal() show_menu_signal = QtCore.Signal()
# Dukto signals for thread-safety
peer_added_signal = QtCore.Signal(object)
peer_removed_signal = QtCore.Signal(object)
incoming_connection_signal = QtCore.Signal(str, object)
dukto_error_signal = QtCore.Signal(str)
transfer_progress_signal = QtCore.Signal(int, int)
transfer_complete_signal = QtCore.Signal(str)
text_received_signal = QtCore.Signal(str, str)
def __init__(self, restart=False, no_quit=False, super_menu=True): def __init__(self, restart=False, no_quit=False, super_menu=True):
super().__init__() super().__init__()
@@ -353,7 +344,6 @@ class MainWindow(QtWidgets.QMainWindow):
self.setMask(mask) self.setMask(mask)
self.super_menu = super_menu self.super_menu = super_menu
self.progress_dialog = None
self.tray = QtWidgets.QSystemTrayIcon(self) self.tray = QtWidgets.QSystemTrayIcon(self)
self.tray.setIcon(QtGui.QIcon(str(ASSET))) self.tray.setIcon(QtGui.QIcon(str(ASSET)))
@@ -363,8 +353,6 @@ class MainWindow(QtWidgets.QMainWindow):
right_menu.addAction("Launch App", self.start_app_launcher) right_menu.addAction("Launch App", self.start_app_launcher)
right_menu.addAction("Search Files", self.start_file_search) right_menu.addAction("Search Files", self.start_file_search)
right_menu.addAction("Search Web", self.start_web_search) right_menu.addAction("Search Web", self.start_web_search)
self.dukto_peers_menu_right = right_menu.addMenu("Dukto")
self.dukto_peers_menu_right.setEnabled(False)
right_menu.addSeparator() right_menu.addSeparator()
right_menu.addAction("Check for updates", self.update_git) right_menu.addAction("Check for updates", self.update_git)
if restart: if restart:
@@ -372,7 +360,7 @@ class MainWindow(QtWidgets.QMainWindow):
right_menu.addAction("Hide/Show", self.toggle_visible) right_menu.addAction("Hide/Show", self.toggle_visible)
right_menu.addSeparator() right_menu.addSeparator()
if not no_quit: if not no_quit:
right_menu.addAction("Quit", self.quit_application) right_menu.addAction("Quit", QtWidgets.QApplication.quit)
self.tray.setContextMenu(right_menu) self.tray.setContextMenu(right_menu)
self.tray.activated.connect(self.handle_tray_activated) self.tray.activated.connect(self.handle_tray_activated)
self.tray.show() self.tray.show()
@@ -382,11 +370,6 @@ class MainWindow(QtWidgets.QMainWindow):
self.left_menu.addAction("Launch App", self.start_app_launcher) self.left_menu.addAction("Launch App", self.start_app_launcher)
self.left_menu.addAction("Search Files", self.start_file_search) self.left_menu.addAction("Search Files", self.start_file_search)
self.left_menu.addAction("Search Web", self.start_web_search) self.left_menu.addAction("Search Web", self.start_web_search)
self.dukto_peers_menu_left = self.left_menu.addMenu("Dukto")
self.dukto_peers_menu_left.setEnabled(False)
# Dukto peer menu storage
self.dukto_peer_menus = {}
# always on top timer # always on top timer
self.stay_on_top_timer = QtCore.QTimer(self) self.stay_on_top_timer = QtCore.QTimer(self)
@@ -397,43 +380,6 @@ class MainWindow(QtWidgets.QMainWindow):
self.show_menu_signal.connect(self.show_menu) self.show_menu_signal.connect(self.show_menu)
self.start_hotkey_listener() self.start_hotkey_listener()
# Init Dukto Protocol
self.init_dukto()
def init_dukto(self):
self.dukto = DuktoProtocol()
# Connect signals to slots
self.peer_added_signal.connect(self.add_dukto_peer_slot)
self.peer_removed_signal.connect(self.remove_dukto_peer_slot)
self.incoming_connection_signal.connect(self.handle_incoming_transfer_slot)
self.dukto_error_signal.connect(self.handle_dukto_error_slot)
self.transfer_progress_signal.connect(self.update_transfer_progress_slot)
self.transfer_complete_signal.connect(self.finish_transfer_slot)
self.text_received_signal.connect(self.show_received_text_slot)
# Assign callbacks that emit signals
self.dukto.on_peer_added = lambda peer: self.peer_added_signal.emit(peer)
self.dukto.on_peer_removed = lambda peer: self.peer_removed_signal.emit(peer)
self.dukto.on_incoming_connection = lambda ip, conn: self.incoming_connection_signal.emit(ip, conn)
self.dukto.on_error = lambda msg: self.dukto_error_signal.emit(msg)
def on_receive_start(sender_ip):
peer_name = self.dukto.peers.get(sender_ip, Peer(sender_ip, sender_ip)).signature
# Use QTimer to ensure this runs on the main thread
QtCore.QTimer.singleShot(0, lambda: self.start_transfer_progress_slot(f"Receiving from {peer_name}..."))
self.dukto.on_receive_start = on_receive_start
self.dukto.on_transfer_progress = lambda total, current: self.transfer_progress_signal.emit(current, total)
self.dukto.on_receive_complete = lambda files, size: self.transfer_complete_signal.emit("Files received successfully!")
self.dukto.on_send_complete = lambda files: self.transfer_complete_signal.emit("Files sent successfully!")
self.dukto.on_receive_text = lambda text, size: self.text_received_signal.emit(
self.dukto.peers.get("unknown", Peer("unknown", "An unknown user")).signature, text
)
self.dukto.initialize()
self.dukto.say_hello()
def show_menu(self): def show_menu(self):
self.left_menu.popup(QtGui.QCursor.pos()) self.left_menu.popup(QtGui.QCursor.pos())
@@ -572,99 +518,14 @@ class MainWindow(QtWidgets.QMainWindow):
QtWidgets.QMessageBox.critical(self, "Update Failed", message) QtWidgets.QMessageBox.critical(self, "Update Failed", message)
def restart_application(self): def restart_application(self):
self.dukto.shutdown()
presence.end() presence.end()
args = [sys.executable] + sys.argv args = [sys.executable] + sys.argv
subprocess.Popen(args) subprocess.Popen(args)
QtWidgets.QApplication.quit() QtWidgets.QApplication.quit()
def quit_application(self):
self.dukto.shutdown()
QtWidgets.QApplication.quit()
# --- Dukto Slots ---
def add_dukto_peer_slot(self, peer: Peer):
if not self.dukto_peers_menu_left.isEnabled():
self.dukto_peers_menu_left.setEnabled(True)
self.dukto_peers_menu_right.setEnabled(True)
peer_menu_left = QtWidgets.QMenu(peer.signature, self.dukto_peers_menu_left)
peer_menu_right = QtWidgets.QMenu(peer.signature, self.dukto_peers_menu_right)
for menu in [peer_menu_left, peer_menu_right]:
send_files_action = menu.addAction("Send Files...")
send_files_action.triggered.connect(lambda: self.send_files_to_peer(peer))
send_text_action = menu.addAction("Send Text...")
send_text_action.triggered.connect(lambda: self.send_text_to_peer(peer))
self.dukto_peers_menu_left.addMenu(peer_menu_left)
self.dukto_peers_menu_right.addMenu(peer_menu_right)
self.dukto_peer_menus[peer.address] = (peer_menu_left, peer_menu_right)
def remove_dukto_peer_slot(self, peer: Peer):
if peer.address in self.dukto_peer_menus:
menus = self.dukto_peer_menus.pop(peer.address)
self.dukto_peers_menu_left.removeAction(menus[0].menuAction())
self.dukto_peers_menu_right.removeAction(menus[1].menuAction())
if not self.dukto_peer_menus:
self.dukto_peers_menu_left.setEnabled(False)
self.dukto_peers_menu_right.setEnabled(False)
def handle_incoming_transfer_slot(self, sender_ip: str, conn: socket.socket):
peer = self.dukto.peers.get(sender_ip)
peer_name = peer.signature if peer else sender_ip
reply = QtWidgets.QMessageBox.question(self, "Incoming Transfer",
f"Accept incoming files from {peer_name}?",
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
QtWidgets.QMessageBox.StandardButton.Yes)
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
self.dukto.start_receiving(conn, sender_ip)
else:
conn.close()
def send_files_to_peer(self, peer: Peer):
file_dialog = QtWidgets.QFileDialog(self)
file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.ExistingFiles)
if file_dialog.exec():
files = file_dialog.selectedFiles()
if files:
self.start_transfer_progress_slot(f"Sending to {peer.signature}...")
self.dukto.send_file(peer.address, files, peer.port)
def send_text_to_peer(self, peer: Peer):
text, ok = QtWidgets.QInputDialog.getMultiLineText(self, "Send Text", f"Enter text to send to {peer.signature}:")
if ok and text:
self.dukto.send_text(peer.address, text, peer.port)
def handle_dukto_error_slot(self, message: str):
QtWidgets.QMessageBox.critical(self, "Dukto Error", message)
if self.progress_dialog:
self.progress_dialog.close()
self.progress_dialog = None
def start_transfer_progress_slot(self, title: str):
self.progress_dialog = QtWidgets.QProgressDialog(title, "Cancel", 0, 100, self)
self.progress_dialog.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
self.progress_dialog.setValue(0)
self.progress_dialog.show()
def update_transfer_progress_slot(self, current: int, total: int):
if self.progress_dialog:
self.progress_dialog.setMaximum(total)
self.progress_dialog.setValue(current)
def finish_transfer_slot(self, message: str):
if self.progress_dialog:
self.progress_dialog.close()
self.progress_dialog = None
QtWidgets.QMessageBox.information(self, "Transfer Complete", message)
def show_received_text_slot(self, sender_name: str, text: str):
QtWidgets.QMessageBox.information(self, f"Text from {sender_name}", text)
def main(): def main():
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
@@ -686,7 +547,6 @@ def main():
pet.show() pet.show()
app.aboutToQuit.connect(presence.end) app.aboutToQuit.connect(presence.end)
app.aboutToQuit.connect(pet.dukto.shutdown)
sys.exit(app.exec()) sys.exit(app.exec())