Fbrowser/python-src/bsshpy.py
stan44 565be4e1e7 Migrate Fbrowser to Rust and Tauri desktop app
- Replace the Python/Docker setup with a Rust workspace and Tauri frontend
- Add core crates for archive, audio, MIDI, plugin, and desktop UI layers
- Refresh the app scaffolding, build config, and documentation
2026-03-30 16:18:26 -05:00

172 lines
6.3 KiB
Python

import sys
import os
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QPushButton, QTextEdit, QFileDialog, QLabel)
from PyQt6.QtCore import QThread, pyqtSignal
import paramiko
class SSHThread(QThread):
output_received = pyqtSignal(str)
def __init__(self, client, command):
super().__init__()
self.client = client
self.command = command
def run(self):
stdin, stdout, stderr = self.client.exec_command(self.command)
for line in stdout:
self.output_received.emit(line.strip())
for line in stderr:
self.output_received.emit(line.strip())
class SSHWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Enhanced SSH Frontend")
self.setGeometry(100, 100, 800, 600)
self.client = None
central_widget = QWidget()
layout = QVBoxLayout()
connection_layout = QHBoxLayout()
self.host_input = QLineEdit()
self.host_input.setPlaceholderText("Host")
connection_layout.addWidget(self.host_input)
self.user_input = QLineEdit()
self.user_input.setPlaceholderText("Username")
connection_layout.addWidget(self.user_input)
self.password_input = QLineEdit()
self.password_input.setPlaceholderText("Password")
self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
connection_layout.addWidget(self.password_input)
self.connect_button = QPushButton("Connect")
self.connect_button.clicked.connect(self.connect_ssh)
connection_layout.addWidget(self.connect_button)
self.disconnect_button = QPushButton("Disconnect")
self.disconnect_button.clicked.connect(self.disconnect_ssh)
self.disconnect_button.setEnabled(False)
connection_layout.addWidget(self.disconnect_button)
layout.addLayout(connection_layout)
self.output_area = QTextEdit()
self.output_area.setReadOnly(True)
layout.addWidget(self.output_area)
command_layout = QHBoxLayout()
self.command_input = QLineEdit()
self.command_input.setPlaceholderText("Enter command")
command_layout.addWidget(self.command_input)
self.execute_button = QPushButton("Execute")
self.execute_button.clicked.connect(self.execute_command)
command_layout.addWidget(self.execute_button)
layout.addLayout(command_layout)
file_transfer_layout = QHBoxLayout()
self.upload_button = QPushButton("Upload File")
self.upload_button.clicked.connect(self.upload_file)
file_transfer_layout.addWidget(self.upload_button)
self.download_button = QPushButton("Download File")
self.download_button.clicked.connect(self.download_file)
file_transfer_layout.addWidget(self.download_button)
layout.addLayout(file_transfer_layout)
self.status_label = QLabel("Not connected")
layout.addWidget(self.status_label)
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def connect_ssh(self):
host = self.host_input.text()
username = self.user_input.text()
password = self.password_input.text()
try:
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.client.connect(hostname=host, username=username, password=password)
self.status_label.setText(f"Connected to {host}")
self.connect_button.setEnabled(False)
self.disconnect_button.setEnabled(True)
self.output_area.append(f"Connected to {host}\n")
except paramiko.AuthenticationException:
self.output_area.append("Authentication failed.\n")
except Exception as e:
self.output_area.append(f"Error: {str(e)}\n")
def disconnect_ssh(self):
if self.client:
self.client.close()
self.client = None
self.status_label.setText("Not connected")
self.connect_button.setEnabled(True)
self.disconnect_button.setEnabled(False)
self.output_area.append("Disconnected\n")
def execute_command(self):
if not self.client:
self.output_area.append("Not connected. Please connect first.\n")
return
command = self.command_input.text()
self.output_area.append(f"$ {command}\n")
self.command_input.clear()
self.ssh_thread = SSHThread(self.client, command)
self.ssh_thread.output_received.connect(self.update_output)
self.ssh_thread.start()
def update_output(self, output):
self.output_area.append(output + "\n")
def upload_file(self):
if not self.client:
self.output_area.append("Not connected. Please connect first.\n")
return
file_path, _ = QFileDialog.getOpenFileName(self, "Select File to Upload")
if file_path:
try:
sftp = self.client.open_sftp()
remote_path = f"/home/{self.user_input.text()}/{os.path.basename(file_path)}"
sftp.put(file_path, remote_path)
sftp.close()
self.output_area.append(f"Uploaded {file_path} to {remote_path}\n")
except Exception as e:
self.output_area.append(f"Upload failed: {str(e)}\n")
def download_file(self):
if not self.client:
self.output_area.append("Not connected. Please connect first.\n")
return
remote_path, ok = QFileDialog.getText(self, "Download File", "Enter remote file path:")
if ok and remote_path:
try:
sftp = self.client.open_sftp()
local_path, _ = QFileDialog.getSaveFileName(self, "Save File As")
if local_path:
sftp.get(remote_path, local_path)
sftp.close()
self.output_area.append(f"Downloaded {remote_path} to {local_path}\n")
except Exception as e:
self.output_area.append(f"Download failed: {str(e)}\n")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SSHWindow()
window.show()
sys.exit(app.exec())