Commit 4c26f5ad authored by Robocoders's avatar Robocoders

Made Tkinter interface configurable using config.ini

parent e2d6d245
...@@ -18,3 +18,11 @@ toggle_others = smblur_shine ...@@ -18,3 +18,11 @@ toggle_others = smblur_shine
tease = smblur_tease tease = smblur_tease
tease_all = smblur_teaseall tease_all = smblur_teaseall
open = smblur_clean open = smblur_clean
[Tkinter]
window_title = SHM Cam Studio
window_width = 300
window_height = 400
button_width = 20
button_height = 2
font_size = 12
...@@ -33,15 +33,15 @@ import vlc ...@@ -33,15 +33,15 @@ import vlc
# Read configuration # Read configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read('config.ini') config.read("config.ini")
# Setup logging # Setup logging
log_file = config.get('General', 'log_file', fallback='/tmp/streaming_control.log') log_file = config.get("General", "log_file", fallback="/tmp/streaming_control.log")
log_level = config.get('General', 'log_level', fallback='INFO') log_level = config.get("General", "log_level", fallback="INFO")
logging.basicConfig( logging.basicConfig(
level=getattr(logging, log_level), level=getattr(logging, log_level),
format='%(asctime)s - %(levelname)s - %(message)s', format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[ handlers=[
RotatingFileHandler(log_file, maxBytes=1024*1024, backupCount=5), RotatingFileHandler(log_file, maxBytes=1024*1024, backupCount=5),
logging.StreamHandler(sys.stdout) logging.StreamHandler(sys.stdout)
...@@ -53,7 +53,7 @@ logger = logging.getLogger(__name__) ...@@ -53,7 +53,7 @@ logger = logging.getLogger(__name__)
flask_app = Flask(__name__) flask_app = Flask(__name__)
# Command Mapping # Command Mapping
COMMANDS = dict(config['Commands']) COMMANDS = dict(config["Commands"])
def run_command(command): def run_command(command):
try: try:
...@@ -65,13 +65,13 @@ def run_command(command): ...@@ -65,13 +65,13 @@ def run_command(command):
logger.error(f"Error executing command {command}: {e}") logger.error(f"Error executing command {command}: {e}")
return f"Error: {e}" return f"Error: {e}"
@flask_app.route('/') @flask_app.route("/")
def index(): def index():
return render_template('index.html', commands=COMMANDS) return render_template("index.html", commands=COMMANDS)
@flask_app.route('/execute', methods=['POST']) @flask_app.route("/execute", methods=["POST"])
def execute(): def execute():
command_key = request.form.get('command') command_key = request.form.get("command")
if command_key in COMMANDS: if command_key in COMMANDS:
result = run_command(COMMANDS[command_key]) result = run_command(COMMANDS[command_key])
...@@ -79,13 +79,13 @@ def execute(): ...@@ -79,13 +79,13 @@ def execute():
else: else:
return "Invalid command", 400 return "Invalid command", 400
@flask_app.route('/stream') @flask_app.route("/stream")
def stream(): def stream():
stream_url = config.get('Web', 'stream_url', fallback="https://192.168.42.1/HLS/record/Live.m3u8") stream_url = config.get("Web", "stream_url", fallback="https://192.168.42.1/HLS/record/Live.m3u8")
return render_template('stream.html', stream_url=stream_url) return render_template("stream.html", stream_url=stream_url)
def create_daemon(): def create_daemon():
if os.name == 'posix': # Unix-like systems if os.name == "posix": # Unix-like systems
try: try:
# First fork # First fork
pid = os.fork() pid = os.fork()
...@@ -93,11 +93,11 @@ def create_daemon(): ...@@ -93,11 +93,11 @@ def create_daemon():
# Exit first parent # Exit first parent
sys.exit(0) sys.exit(0)
except OSError as err: except OSError as err:
logger.error(f'Fork #1 failed: {err}') logger.error(f"Fork #1 failed: {err}")
sys.exit(1) sys.exit(1)
# Decouple from parent environment # Decouple from parent environment
os.chdir('/') os.chdir("/")
os.setsid() os.setsid()
os.umask(0) os.umask(0)
...@@ -108,211 +108,53 @@ def create_daemon(): ...@@ -108,211 +108,53 @@ def create_daemon():
# Exit from second parent # Exit from second parent
sys.exit(0) sys.exit(0)
except OSError as err: except OSError as err:
logger.error(f'Fork #2 failed: {err}') logger.error(f"Fork #2 failed: {err}")
sys.exit(1) sys.exit(1)
# Redirect standard file descriptors # Redirect standard file descriptors
sys.stdout.flush() sys.stdout.flush()
sys.stderr.flush() sys.stderr.flush()
si = open(os.devnull, 'r') si = open(os.devnull, "r")
so = open(os.devnull, 'a+') so = open(os.devnull, "a+")
se = open(os.devnull, 'a+') se = open(os.devnull, "a+")
os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno()) os.dup2(se.fileno(), sys.stderr.fileno())
elif os.name == 'nt': # Windows
try:
# Hide the console window
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
# Start the script as a new process
subprocess.Popen([sys.executable, __file__],
startupinfo=si,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
# Exit the current process
sys.exit(0)
except Exception as err:
logger.error(f'Failed to create background process: {err}')
sys.exit(1)
else:
logger.error(f'Unsupported operating system: {os.name}')
sys.exit(1)
def check_port_available(port):
"""Check if a port is available"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(('localhost', port)) != 0
def run_flask_app(port=5000, daemon_mode=False):
"""Run Flask app with optional daemon mode"""
if not check_port_available(port):
logger.error(f"Port {port} is already in use")
sys.exit(1)
logger.info(f"Starting Flask app on port {port}")
if daemon_mode and sys.platform != 'win32':
create_daemon()
flask_app.run(host='0.0.0.0', port=port, debug=False, use_reloader=False)
class VideoPlayer:
def __init__(self, master, video_url):
self.master = master
# VLC player setup
args = []
_isLinux = sys.platform.startswith('linux')
if _isLinux:
args.append('--vout=mmal_vout')
# Create a VLC instance
self.Instance = vlc.Instance(args)
# Create a new MediaPlayer
self.player = self.Instance.media_player_new()
# Set the media
media = self.Instance.media_new(video_url)
self.player.set_media(media)
# Create a frame for the video
self.video_frame = self.master
self.video_frame.pack(fill=tk.BOTH, expand=True)
self.canvas = tk.Canvas(self.video_frame, width=800)
self.canvas.pack(fill=tk.BOTH, expand=True)
# Embed the VLC Video
win_id = self.canvas.winfo_id()
if _isLinux:
self.player.set_xwindow(win_id)
else:
self.player.set_hwnd(win_id)
# Play the video
self.player.play()
def create_tkinter_gui(): def create_tkinter_interface():
# Create the main window root = tk.Tk()
window = tk.Tk() root.title(config.get("Tkinter", "window_title", fallback="SHM Cam Studio"))
window.title("Streaming Control Panel") root.geometry(f"{config.get(Tkinter, window_width, fallback=300)}x{config.get(Tkinter, window_height, fallback=400)}")
helv36 = tkFont.Font(family='Helvetica', size=13, weight='bold') button_font = tkFont.Font(size=int(config.get("Tkinter", "font_size", fallback="12")))
button_width = int(config.get("Tkinter", "button_width", fallback="20"))
button_height = int(config.get("Tkinter", "button_height", fallback="2"))
# Frame for the left side for command_name, command in COMMANDS.items():
fleft = tk.Frame(window) button = tk.Button(root, text=command_name, command=lambda cmd=command: run_command(cmd),
fleft.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) font=button_font, width=button_width, height=button_height)
button.pack(pady=5)
# URL of your HLS stream root.mainloop()
video_url = "rtmp://192.168.42.1/record/Live"
VideoPlayer(fleft, video_url)
# Frame for the right side def run_flask():
fright = tk.Frame(window) port = config.get("Web", "port", fallback="5000")
fright.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) flask_app.run(host="0.0.0.0", port=int(port))
# Frame for the first two rows in the right frame if __name__ == "__main__":
frame1 = tk.Frame(fright) parser = argparse.ArgumentParser(description="SHM Cam Studio")
frame1.pack(fill=tk.BOTH, expand=True) parser.add_argument("--daemon", action="store_true", help="Run as daemon")
# Buttons configuration
buttons_row0 = [
('PRIVATE STEFY', 'smblur_private_stefy'),
('PRIVATE LEELOO', 'smblur_private_leeloo'),
('PRIVATE JASMIN', 'smblur_private_jasmin'),
('PRIVATE OTHER', 'smblur_private')
]
buttons_row1 = [
('OPEN/CLOSE STEFY', 'smblur_stefy'),
('OPEN/CLOSE LEELOO', 'smblur_leeloo'),
('OPEN/CLOSE JASMIN', 'smblur_jasmin'),
('OPEN/CLOSE OTHERS', 'smblur_shine')
]
# Create buttons for the first two rows
for j, (text, command) in enumerate(buttons_row0):
button = tk.Button(frame1, text=text, font=helv36, width=25, height=15, bg="green", fg="white",
command=lambda cmd=command: run_command(cmd))
button.grid(row=0, column=j, sticky='nsew')
for j, (text, command) in enumerate(buttons_row1):
button = tk.Button(frame1, text=text, font=helv36, width=25, height=15, bg="green", fg="white",
command=lambda cmd=command: run_command(cmd))
button.grid(row=1, column=j, sticky='nsew')
# Configure the columns in the first frame
for i in range(4):
frame1.grid_columnconfigure(i, weight=1)
# Frame for the third row in the right frame
frame2 = tk.Frame(fright)
frame2.pack(fill=tk.BOTH, expand=True)
# Row 2 with 3 buttons
buttons_row2 = [
('TEASE', 'smblur_tease'),
('TEASE ALL', 'smblur_teaseall'),
('OPEN', 'smblur_clean')
]
# Create buttons for the third row
for j, (text, command) in enumerate(buttons_row2):
button = tk.Button(frame2, text=text, font=helv36, width=30, height=25, bg="blue", fg="white",
command=lambda cmd=command: run_command(cmd))
button.grid(row=0, column=j, sticky='nsew')
# Configure the columns in the second frame
for i in range(3):
frame2.grid_columnconfigure(i, weight=1)
# Add a button to open web interface
web_button = tk.Button(frame2, text="Open Web Interface",
command=lambda: webbrowser.open('http://localhost:5000'),
bg="purple", fg="white", font=helv36)
web_button.grid(row=1, column=1, sticky='nsew')
return window
def main():
# Setup argument parser
parser = argparse.ArgumentParser(description='Streaming Control Panel')
parser.add_argument('--web-only', action='store_true',
help='Start only the web interface')
parser.add_argument('--daemon', action='store_true',
help='Run in daemon mode (Unix-like systems only)')
parser.add_argument('--port', type=int, default=5000,
help='Port for the web interface (default: 5000)')
# Parse arguments
args = parser.parse_args() args = parser.parse_args()
try: if args.daemon:
# Daemon mode for web interface create_daemon()
if args.web_only or args.daemon:
run_flask_app(port=args.port, daemon_mode=args.daemon) flask_thread = threading.Thread(target=run_flask)
else: flask_thread.start()
# Start web interface in a background thread
web_thread = threading.Thread(
target=run_flask_app,
kwargs={'port': args.port},
daemon=True
)
web_thread.start()
# Launch Tkinter GUI create_tkinter_interface()
window = create_tkinter_gui()
window.mainloop()
except Exception as e: # Cleanup
logger.error(f"Unexpected error: {e}") flask_thread.join()
sys.exit(1)
if __name__ == '__main__':
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment