This directory is empty
No files or folders to display
| Name | Size | Type |
|---|---|---|
| .. (Parent Directory) | - | Directory |
| {dirname} | - | Directory |
| {filename} | {formatted_size} | {file_type} |
#!/usr/bin/env python3 """ Enhanced HTTP Server with Range Request Support for Media Streaming Supports HTTP Range requests to enable seeking in audio/video files. Can serve multiple directories mapped to different URL paths. Uses ThreadingHTTPServer for improved performance with multiple connections. """ import http.server import socketserver import argparse import os import socket import urllib.parse from datetime import datetime import sys import mimetypes import re # Global variables DIRECTORIES = {} PORT = 8000 def get_local_ip_addresses(): """Get a list of all local IP addresses on the machine.""" try: hostname = socket.gethostname() addresses = socket.getaddrinfo(hostname, None) ipv4_addresses = [addr[4][0] for addr in addresses if addr[0] == socket.AF_INET] return list(set(ipv4_addresses)) except Exception as e: print(f"Error getting IP addresses: {e}") return [] class RangeHTTPHandler(http.server.SimpleHTTPRequestHandler): """HTTP request handler that supports Range requests for media streaming.""" def log_message(self, format, *args): """Override to provide more detailed logs.""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") client_address = self.client_address[0] message = format % args print(f"[{timestamp}] {client_address} - {message}") def translate_path(self, path): """Override the path translation to use our directory mappings.""" parsed_path = urllib.parse.urlparse(path) path = parsed_path.path # Handle multiple URL encoding layers original_path = path while True: decoded = urllib.parse.unquote(path) if decoded == path: break path = decoded if path == "/" or path == "/server-info": return super().translate_path(urllib.parse.unquote(self.path)) if path.startswith('/C/Users/') or path.startswith('/c/Users/'): windows_path = path[1].upper() + ":" + path[2:].replace('/', '\\') return windows_path for url_path, fs_path in DIRECTORIES.items(): if path.startswith(url_path): relative_path = path[len(url_path):] if not relative_path.startswith('/'): relative_path = '/' + relative_path normalized_path = os.path.normpath(relative_path).lstrip('/') result = os.path.join(fs_path, normalized_path) return result return super().translate_path(original_path) def parse_range_header(self, range_header, file_size): """Parse the Range header and return start and end byte positions.""" range_match = re.match(r'bytes=(\d*)-(\d*)', range_header) if not range_match: return None start_str, end_str = range_match.groups() if start_str and end_str: start = int(start_str) end = int(end_str) elif start_str and not end_str: start = int(start_str) end = file_size - 1 elif not start_str and end_str: start = file_size - int(end_str) end = file_size - 1 else: return None if start < 0 or end >= file_size or start > end: return None return start, end def send_range_response(self, file_path, range_header): """Send a partial content response for range requests.""" try: file_size = os.path.getsize(file_path) range_result = self.parse_range_header(range_header, file_size) if range_result is None: self.send_error(416, "Range Not Satisfiable") self.send_header("Content-Range", f"bytes */{file_size}") self.end_headers() return start, end = range_result content_length = end - start + 1 self.send_response(206, "Partial Content") content_type = mimetypes.guess_type(file_path)[0] or 'application/octet-stream' self.send_header("Content-Type", content_type) self.send_header("Content-Length", str(content_length)) self.send_header("Content-Range", f"bytes {start}-{end}/{file_size}") self.send_header("Accept-Ranges", "bytes") # Remove cache control headers for better performance self.end_headers() with open(file_path, 'rb') as f: f.seek(start) remaining = content_length while remaining > 0: chunk_size = min(65536, remaining) # 64KB chunks for better performance chunk = f.read(chunk_size) if not chunk: break self.wfile.write(chunk) remaining -= len(chunk) except Exception as e: self.send_error(500, f"Error serving range request: {str(e)}") def format_file_size(self, size_bytes): """Format file size in human readable format.""" if size_bytes == 0: return "0 B" size_names = ["B", "KB", "MB", "GB", "TB"] import math i = int(math.floor(math.log(size_bytes, 1024))) p = math.pow(1024, i) s = round(size_bytes / p, 2) return f"{s} {size_names[i]}" def get_file_icon(self, filename, is_dir=False): """Get appropriate icon for file type.""" if is_dir: return "📁" ext = os.path.splitext(filename)[1].lower() if ext in ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.m4v']: return "🎬" elif ext in ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma', '.m4a']: return "🎵" elif ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg', '.webp']: return "🖼️" elif ext in ['.pdf', '.doc', '.docx', '.txt', '.rtf']: return "📄" elif ext in ['.zip', '.rar', '.7z', '.tar', '.gz']: return "📦" else: return "📄" def is_media_file(self, filename): """Check if file is a media file.""" ext = os.path.splitext(filename)[1].lower() return ext in ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma', '.m4a'] def create_directory_listing(self, path, file_path): """Create an enhanced directory listing page.""" try: file_list = os.listdir(file_path) except OSError: self.send_error(404, "Directory not found") return dirs = [] files = [] for item in file_list: item_path = os.path.join(file_path, item) if os.path.isdir(item_path): dirs.append(item) else: files.append(item) dirs.sort(key=str.lower) files.sort(key=str.lower) parent_path = "" if path != "/" and len(path.strip("/")) > 0: path_parts = path.strip("/").split("/") if len(path_parts) > 1: parent_path = "/" + "/".join(path_parts[:-1]) else: parent_path = "/" decoded_path = urllib.parse.unquote(path) html = f"""
No files or folders to display
| Name | Size | Type |
|---|---|---|
| .. (Parent Directory) | - | Directory |
| {dirname} | - | Directory |
| {filename} | {formatted_size} | {file_type} |
Server is running on: {local_ip}:{PORT}
Available interfaces: {', '.join(get_local_ip_addresses())}
✅ HTTP Range Requests: Enabled - supports seeking in audio/video files
✅ Media Types: MP4, MP3, AVI, MKV, WebM, OGG, WAV, FLAC, and more
✅ Browser Compatibility: Works with HTML5 video/audio players
| URL Path | File System Path | Links |
|---|---|---|
| {url_path} | {fs_path} | Browse |
| /C/Users/ | C:\\Users\\ | Browse |