Latest Development Copy

This commit is contained in:
2025-09-21 10:19:44 -04:00
parent f59b3794ea
commit a480ca0bf3
3 changed files with 150 additions and 12 deletions

37
src/#__main__.py# Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python3
import curses
import argparse
import sys
from curses import wrapper
from .libs.config import Config
from .libs.absapi import ABSApi, Endpoint, ABSResponse, Library
from .libs.terminal import Terminal, ExitTerminal
parser = argparse.ArgumentParser()
_: argparse.Action = parser.add_argument("--debug", help="Debug the program", action="store_true")
args = parser.parse_args()
config: Config = Config()
# stdscr: curses.window = curses.initscr()
def main(stdscr: curses.window) -> None:
terminal: Terminal = Terminal(stdscr)
Terminal.startup(terminal)
if __name__ == "__main__":
if args.debug:
_api = ABSApi()
_response: list[Library]|None = _api.get_libraries()
_id = _response[0].id if _response else ""
_books = _api.get_books(_id)
for book in _books if _books else []:
breakpoint()
print(book.media.metadata.title)
sys.exit(0)
try:
while True:
wrapper(main)
except ExitTerminal:
pass

View File

@@ -9,11 +9,20 @@ from .libs.terminal import Terminal, ExitTerminal
parser = argparse.ArgumentParser()
_: argparse.Action = parser.add_argument("--debug", help="Debug the program", action="store_true")
_: argparse.Action = parser.add_argument("--remote-debug", help="Enable remote debugging with pudb", action="store_true")
_: argparse.Action = parser.add_argument("--debug-port", help="Port for remote debugging", type=int, default=6899)
args = parser.parse_args()
config: Config = Config()
# stdscr: curses.window = curses.initscr()
def main(stdscr: curses.window) -> None:
if args.remote_debug:
import os
from pudb.remote import set_trace
_term_size: os.terminal_size = os.get_terminal_size()
# set_trace(term_size=(_term_size.columns, _term_size.lines), host='0.0.0.0', port=args.debug_port)
terminal: Terminal = Terminal(stdscr, remote_debug=args.remote_debug, debug_port=args.debug_port)
else:
terminal: Terminal = Terminal(stdscr)
Terminal.startup(terminal)

View File

@@ -6,9 +6,11 @@ class ExitTerminal(Exception):
pass
class Terminal:
def __init__(self, stdscr: curses.window, running: bool = True) -> None:
def __init__(self, stdscr: curses.window, running: bool = True, remote_debug: bool = False, debug_port: int = 6899) -> None:
self.stdscr: curses.window = stdscr
self.running: bool = running
self.remote_debug: bool = remote_debug
self.debug_port: int = debug_port
self.api: ABSApi = ABSApi()
self.height: int = self.stdscr.getmaxyx()[0]
self.width: int = self.stdscr.getmaxyx()[1]
@@ -28,6 +30,11 @@ class Terminal:
]
self.active_window: str = "library"
self.playing: bool = False
self.row: int = 0
self.book_count: int = 0
self.libraries: list[Library]|None = None
self.books: list[Book]|None = None
self.library_pad: curses.window|None = None
def alignC(self, text: str) -> int:
return (self.width // 2) - (len(text) // 2)
@@ -35,9 +42,41 @@ class Terminal:
def alignR(self, text: str) -> int:
return (self.height // 2) - (len(text) // 2)
def pad_content(self, strings: list[str], columns: int) -> list[str]:
col_width = self.width // columns
padded_strings = []
for i, s in enumerate(strings):
if i < len(strings) - 1: # Don't pad the last column
padded_strings.append(s.ljust(col_width))
else:
padded_strings.append(s)
return padded_strings
def initialize_colors(self) -> None:
if curses.has_colors() and curses.can_change_color():
curses.start_color()
curses.init_color(17, 500, 500, 900)
curses.init_pair(1, curses.COLOR_WHITE, 17)
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_CYAN)
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)
curses.init_pair(4, curses.COLOR_WHITE, curses.COLOR_BLACK)
elif curses.has_colors():
curses.start_color()
curses.use_default_colors()
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_CYAN)
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)
curses.init_pair(4, curses.COLOR_WHITE, curses.COLOR_BLACK)
else:
pass
def startup(self) -> None:
self.stdscr.clear()
self.menu()
self.initialize_colors()
self.stdscr.refresh()
self.library_window()
self.footer()
@@ -76,20 +115,46 @@ class Terminal:
return curses.newwin(self.main_content_height, self.main_content_width,
self.main_content_height + 3, 0)
def highlight_line(self, pad: curses.window, line: int, content: str) -> None:
if curses.has_colors() and curses.can_change_color():
pad.addstr(line, 0, "".join(content), curses.color_pair(17))
elif curses.has_colors():
pad.addstr(line, 0, "".join(content), curses.color_pair(2))
else:
pad.addstr(line, 0, "".join(content), curses.A_STANDOUT)
def library_window(self) -> None:
# Load data if needed
if self.libraries is None:
self.libraries = self.api.get_libraries()
if self.books is None:
self.books = self.api.get_books(self.libraries[0].id) if self.libraries else None
self.book_count = self.books.__len__() if self.books is not None else 0
if self.books is not None:
# Always recreate the pad to update highlighting
self.library_pad = self.new_main_pad(len(self.books))
_y: int = 0
libraries: list[Library]|None = self.api.get_libraries()
books: list[Book]|None = self.api.get_books(libraries[0].id) if libraries else None
if books is not None:
_pad: curses.window = self.new_main_pad(len(books) if libraries else 1)
for book in books:
_pad.addstr(_y, 0, f"- {book.media.metadata.title}\t {book.media.metadata.authorName} \t {book.media.duration}")
for book in self.books:
_row_contents: list[str] = [f"{_y} :: {book.media.metadata.title}", f"{book.media.metadata.seriesName}", f"{book.media.metadata.authorName}", f"{book.media.duration}"]
_padded_contents = self.pad_content(_row_contents, 4)
# Highlight the current row
if _y == self.row:
self.highlight_line(self.library_pad, _y, "".join(_padded_contents))
# self.library_pad.addstr(_y, 0, "".join(_padded_contents), curses.A_REVERSE)
else:
self.library_pad.addstr(_y, 0, "".join(_padded_contents), curses.A_STANDOUT if _y % 2 == 0 else curses.A_NORMAL)
_y += 1
_pad.refresh(0, 0, self.main_content_begin_y, self.main_content_begin_x,
self.main_content_begin_y + self.main_content_height - 1,
self.main_content_begin_x + self.main_content_width - 1)
else:
self.help_window("No books found in the library.")
if self.library_pad is not None:
# Always scroll by one line to create smooth scrolling effect
scroll_offset = max(0, self.row - (self.main_content_height // 2))
self.library_pad.refresh(scroll_offset, 0, self.main_content_begin_y, self.main_content_begin_x,
self.main_content_begin_y + self.main_content_height - 1,
self.main_content_begin_x + self.main_content_width - 1)
self.active_window = "library"
def search_window(self) -> None:
@@ -140,6 +205,22 @@ class Terminal:
self.playing = True
self.footer(f"Playing {self.playing} - Feature not implemented yet.")
def set_remote_breakpoint(self) -> None:
if self.remote_debug:
import os
from pudb.remote import set_trace
_term_size: os.terminal_size = os.get_terminal_size()
set_trace(term_size=(_term_size.columns, _term_size.lines), host='0.0.0.0', port=self.debug_port)
self.stdscr.clear()
self.menu()
self.stdscr.refresh()
self.library_window() if self.active_window == "library" else None
self.search_window() if self.active_window == "search" else None
self.now_playing_window() if self.active_window == "now_playing" else None
self.settings_window() if self.active_window == "settings" else None
self.help_window(None) if self.active_window == "help" else None
self.footer()
def key_handler(self) -> None:
while True:
key = self.stdscr.getch()
@@ -158,3 +239,14 @@ class Terminal:
raise ExitTerminal()
elif key == ord(' '):
self.play_toggle()
elif key == ord("j") and self.active_window == "library":
if self.row < self.book_count - 1: self.row = self.row + 1
else: self.row = 0
self.library_window()
elif key == ord("k") and self.active_window == "library":
if self.row > 0: self.row = self.row - 1
else: self.row = self.book_count - 1
self.library_window()
elif key == ord("d") and self.remote_debug:
self.set_remote_breakpoint()