Initial download server added.
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import subprocess
|
||||
import threading
|
||||
import termios
|
||||
import tty
|
||||
import select
|
||||
import signal
|
||||
import http.server
|
||||
import socketserver
|
||||
from threading import Timer
|
||||
|
||||
# Global variable to store server info for repeat announcement
|
||||
server_info = {'ip': '', 'port': 0, 'httpd': None}
|
||||
|
||||
# Get local IP address
|
||||
def get_local_ip():
|
||||
try:
|
||||
# Create a socket connection to determine the local IP
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
# Doesn't even have to be reachable
|
||||
s.connect(('10.255.255.255', 1))
|
||||
IP = s.getsockname()[0]
|
||||
s.close()
|
||||
except Exception:
|
||||
IP = '127.0.0.1'
|
||||
return IP
|
||||
|
||||
# Announce server start with speech
|
||||
def announce_server_start(ip, port):
|
||||
formatted_ip = ""
|
||||
segments = ip.split(".")
|
||||
for i, segment in enumerate(segments):
|
||||
# Add each digit with space between
|
||||
formatted_ip += " ".join(segment)
|
||||
# Add " dot " between segments (but not after the last segment)
|
||||
if i < len(segments) - 1:
|
||||
formatted_ip += " dot "
|
||||
|
||||
try:
|
||||
message = f"Download server running on {formatted_ip} port {port}"
|
||||
# Using spd-say with -Cw flag for better speech synthesis
|
||||
subprocess.run(['spd-say', '-Cw', message])
|
||||
print(f"Download server running on {ip}:{port}")
|
||||
print("Press any key to repeat server information")
|
||||
print("Press Ctrl+C to stop server")
|
||||
except Exception as e:
|
||||
print(f"Could not announce server start: {e}")
|
||||
print(f"Download server running on {ip}:{port}")
|
||||
|
||||
# Function to repeat the server announcement
|
||||
def repeat_announcement():
|
||||
if server_info['ip'] and server_info['port']:
|
||||
announce_server_start(server_info['ip'], server_info['port'])
|
||||
|
||||
# Keyboard listener function
|
||||
def keyboard_listener():
|
||||
"""Listen for key presses and repeat server announcement"""
|
||||
if not sys.stdin.isatty():
|
||||
print("Not running in a terminal, keyboard listener disabled")
|
||||
return
|
||||
|
||||
try:
|
||||
# Save original terminal settings
|
||||
old_settings = termios.tcgetattr(sys.stdin)
|
||||
|
||||
while True:
|
||||
try:
|
||||
# Set terminal to raw mode to capture individual key presses
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
|
||||
# Use select to check if input is available (non-blocking)
|
||||
if select.select([sys.stdin], [], [], 1) == ([sys.stdin], [], []):
|
||||
# Read a single character
|
||||
key = sys.stdin.read(1)
|
||||
|
||||
# Any key press will trigger the announcement
|
||||
if key:
|
||||
repeat_announcement()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# Handle Ctrl+C gracefully
|
||||
print("\nKeyboard listener interrupted")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Error in keyboard listener: {e}")
|
||||
break
|
||||
finally:
|
||||
# Always restore terminal settings
|
||||
try:
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
print(f"Could not initialize keyboard listener: {e}")
|
||||
|
||||
# Start keyboard listener in background thread
|
||||
def start_keyboard_listener():
|
||||
"""Start the keyboard listener in a daemon thread"""
|
||||
try:
|
||||
listener_thread = threading.Thread(target=keyboard_listener, daemon=True)
|
||||
listener_thread.start()
|
||||
except Exception as e:
|
||||
print(f"Could not start keyboard listener: {e}")
|
||||
|
||||
# Signal handler for graceful shutdown
|
||||
def signal_handler(signum, frame):
|
||||
try:
|
||||
subprocess.run(['spd-say', '-Cw', 'Download server shutting down'])
|
||||
except Exception:
|
||||
pass
|
||||
print("\nDownload server shutting down...")
|
||||
if server_info['httpd']:
|
||||
server_info['httpd'].shutdown()
|
||||
sys.exit(0)
|
||||
|
||||
# Custom HTTP handler to serve files from /home/stormux
|
||||
class DownloadHandler(http.server.SimpleHTTPRequestHandler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Set the directory to serve files from
|
||||
super().__init__(*args, directory='/home/stormux', **kwargs)
|
||||
|
||||
def log_message(self, format, *args):
|
||||
# Log downloads with speech announcement
|
||||
client_ip = self.client_address[0]
|
||||
if 'GET' in format % args and not any(x in format % args for x in ['.ico', '.css', '.js']):
|
||||
try:
|
||||
# Only announce actual file downloads, not directory listings or favicon requests
|
||||
path = args[1] if len(args) > 1 else "unknown"
|
||||
if not path.endswith('/'): # It's a file, not directory
|
||||
filename = os.path.basename(path)
|
||||
subprocess.run(['spd-say', '-Cw', f'File {filename} downloaded by {client_ip}'],
|
||||
timeout=5)
|
||||
except Exception:
|
||||
pass
|
||||
# Still print to console
|
||||
print(format % args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Starting Download Server...")
|
||||
|
||||
# Change to the home directory to serve files from there
|
||||
os.chdir('/home/stormux')
|
||||
|
||||
# Get local IP and set port
|
||||
local_ip = get_local_ip()
|
||||
port = 8000
|
||||
|
||||
# Store server info for repeat announcements
|
||||
server_info['ip'] = local_ip
|
||||
server_info['port'] = port
|
||||
|
||||
# Set up signal handlers for graceful shutdown
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
try:
|
||||
# Create HTTP server
|
||||
with socketserver.TCPServer(("", port), DownloadHandler) as httpd:
|
||||
server_info['httpd'] = httpd
|
||||
|
||||
# Start keyboard listener for repeat announcements
|
||||
start_keyboard_listener()
|
||||
|
||||
# Announce server start
|
||||
announce_server_start(local_ip, port)
|
||||
|
||||
# Start serving files
|
||||
print(f"Serving files from /home/stormux at http://{local_ip}:{port}")
|
||||
httpd.serve_forever()
|
||||
|
||||
except OSError as e:
|
||||
if e.errno == 98: # Address already in use
|
||||
try:
|
||||
subprocess.run(['spd-say', '-Cw', f'Port {port} is already in use'])
|
||||
except Exception:
|
||||
pass
|
||||
print(f"Error: Port {port} is already in use. Please stop any existing servers and try again.")
|
||||
else:
|
||||
try:
|
||||
subprocess.run(['spd-say', '-Cw', 'Download server failed to start'])
|
||||
except Exception:
|
||||
pass
|
||||
print(f"Error starting server: {e}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
try:
|
||||
subprocess.run(['spd-say', '-Cw', 'Download server error'])
|
||||
except Exception:
|
||||
pass
|
||||
print(f"Unexpected error: {e}")
|
||||
sys.exit(1)
|
||||
@@ -1094,6 +1094,7 @@ if __name__ == "__main__":
|
||||
menu.add_item("System", "Set Default Voice", "/usr/local/bin/set-voice.py")
|
||||
menu.add_item("System", "Set Timezone", "GAME='Set Timezone' /home/stormux/.clirc")
|
||||
menu.add_item("System", "Upload Files", "/home/stormux/.local/upload_server/uploader.py")
|
||||
menu.add_item("System", "Download Files", "/home/stormux/.local/download_server.py")
|
||||
menu.add_item("System", "Restart: Can Take Several Minutes", "sudo reboot")
|
||||
menu.add_item("System", "Power Off: Wait 2 Minutes Before Disconnecting Power", "sudo poweroff")
|
||||
# Service menu items will be added dynamically in run() method via update_service_menu_items()
|
||||
|
||||
Reference in New Issue
Block a user