Initial Commit

This commit is contained in:
klein panic
2024-10-25 20:10:38 -04:00
commit 855fdfd414
10 changed files with 2219 additions and 0 deletions

219
src/main.py Normal file
View File

@@ -0,0 +1,219 @@
#!/usr/bin/env python3
# main.py
import curses
import time
from local_music import LocalMusicPlayer
from local_media import LocalMediaPlayer
from spotify_player import SpotifyPlayer
from radio_player import RadioPlayer
class MediaDashboardApp:
def __init__(self, stdscr):
self.stdscr = stdscr
self.monocle_mode = False
self.active_window = 0
self.windows = [
LocalMusicPlayer(self.stdscr),
LocalMediaPlayer(self.stdscr),
SpotifyPlayer(self.stdscr),
RadioPlayer(self.stdscr)
]
self.window_titles = ["Local Music", "Local Media", "Spotify", "Radio"]
self.setup_curses()
def setup_curses(self):
curses.curs_set(0) # Hide the cursor
self.stdscr.nodelay(1)
self.stdscr.timeout(100)
curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
def draw_tiling(self):
self.stdscr.clear()
height, width = self.stdscr.getmaxyx()
mid_y = height // 2
mid_x = width // 2
# Define windows (quadrants), ensuring they fit within the screen dimensions
for idx, module in enumerate(self.windows):
if not module:
continue
# Calculate positions for quadrants
if idx == 0:
row, col = 0, 0
win_height = mid_y
win_width = mid_x
elif idx == 1:
row, col = 0, mid_x
win_height = mid_y
win_width = width - mid_x
elif idx == 2:
row, col = mid_y, 0
win_height = height - mid_y
win_width = mid_x
elif idx == 3:
row, col = mid_y, mid_x
win_height = height - mid_y
win_width = width - mid_x
# Sub-window for each quadrant
subwin = self.stdscr.subwin(win_height, win_width, row, col)
subwin.box()
subwin.addstr(1, 2, self.window_titles[idx] + ":")
# Only render Spotify quadrant if it is the active window
if isinstance(module, SpotifyPlayer) and self.active_window != 2:
continue # Skip rendering Spotify unless its active in monocle
module.render(subwin)
# Draw lines separating the windows
self.stdscr.vline(0, mid_x, curses.ACS_VLINE, height)
self.stdscr.hline(mid_y, 0, curses.ACS_HLINE, width)
self.stdscr.refresh()
def draw_monocle(self):
self.stdscr.clear()
width = self.stdscr.getmaxyx()[1] # Only get the width, as height is not needed
# Draw the active window in monocle mode
module = self.windows[self.active_window]
if not module:
return
title = self.window_titles[self.active_window]
self.stdscr.addstr(0, (width // 2) - (len(title) // 2), f"{title}:", curses.A_BOLD)
try:
module.render(self.stdscr)
except Exception as e:
self.stdscr.addstr(1, 1, f"Error loading {title}: {str(e)}")
logging.error(f"Error rendering module {title}: {str(e)}")
self.stdscr.refresh()
def handle_mouse(self, event):
"""Handle mouse clicks and interactions."""
if self.monocle_mode: #and self.active_window is not None:
module = self.windows[self.active_window]
if module and hasattr(module, 'handle_mouse'):
module.handle_mouse(event)
return
_, x, y, _, button = event
# Basic idea - Determine the clicked quadrant based on coordinates
height, width = self.stdscr.getmaxyx()
mid_y = height // 2
mid_x = width // 2
if button == curses.BUTTON1_CLICKED:
if y < mid_y and x < mid_x:
self.active_window = 0 # Local Music
elif y < mid_y and x >= mid_x:
self.active_window = 1 # Local Media
elif y >= mid_y and x < mid_x:
self.active_window = 2 # Spotify
elif y >= mid_y and x >= mid_x:
self.active_window = 3 # Radio
else:
return
module = self.windows[self.active_window]
if module is not None:
self.monocle_mode = True
# Set current_view to "explorer" for the active windows
module.current_view = "radio" if isinstance(module, RadioPlayer) else "explorer"
# Reset any selection indices if needed
module.selected_index = 0
if isinstance(module, SpotifyPlayer):
module.current_playlist = None
self.draw_monocle()
else:
# Display a message
self.stdscr.addstr(0, 0, "This quadrant is not implemented yet.", curses.A_BOLD)
self.stdscr.refresh()
time.sleep(1)
#pass
def cleanup(self):
"""Clean up resources before exiting."""
for module in self.windows:
if module and hasattr(module, 'stop_media'):
module.stop_media()
if module and hasattr(module, 'stop_station'):
module.stop_station()
def handle_keypress(self, key):
"""Handle keypress actions globally and pass them to active modules."""
module = self.windows[self.active_window]
key_handled = False
if self.monocle_mode and module:
# If module handles the key, skip global handling
key_handled = module.handle_keypress(key)
# Check if the active window wants to exit monocle mode
if hasattr(module, 'current_view') and module.current_view == "exit":
self.monocle_mode = False
self.draw_tiling()
return True
if not key_handled:
if key == ord('q') or key == 27: # Quit on 'q' or 'Esc'
self.cleanup()
return True
elif key == ord('m'): # Monocle mode
self.monocle_mode = True
self.draw_monocle()
return True
elif key == ord('t'): # Tiling mode
self.monocle_mode = False
self.draw_tiling()
elif self.monocle_mode and key == ord('j'):
# Only change monocle window if module is in 'dashboard' view
if module.current_view == 'dashboard' and self.active_window < len(self.windows) - 1:
self.active_window += 1
self.draw_monocle()
elif self.monocle_mode and key == ord('k'):
if module.current_view == 'dashboard' and self.active_window > 0:
self.active_window -= 1
self.draw_monocle()
elif key == curses.KEY_MOUSE:
self.handle_mouse(curses.getmouse())
# Handle back to tiling mode directly if module is in 'dashboard' view
if key in (curses.KEY_BACKSPACE, ord('\b'), 127) and self.monocle_mode:
if module.current_view == 'dashboard':
self.monocle_mode = False
self.draw_tiling()
def main_loop(self):
# Main loop to keep the screen updated
while True:
key = self.stdscr.getch()
# Break out of loop if keypress handler requests it
if self.handle_keypress(key):
break
# Periodically check playback status
if self.monocle_mode and self.active_window is not None:
module = self.windows[self.active_window]
if module and hasattr(module, 'check_playback_status'):
module.check_playback_status()
# Redraw based on current mode
if self.monocle_mode:
self.draw_monocle()
else:
self.draw_tiling()
time.sleep(0.1) # Adjust for responsiveness
def main(stdscr):
app = MediaDashboardApp(stdscr)
app.main_loop()
if __name__ == "__main__":
curses.wrapper(main)