diff --git a/mac_cocoa.py b/mac_cocoa.py index d2c0410..39efe5c 100644 --- a/mac_cocoa.py +++ b/mac_cocoa.py @@ -6,6 +6,8 @@ This is a minimal native UI: Generate Report, Copy to Clipboard, and Debug outpu import os import threading +import json +from pathlib import Path from datetime import datetime from app import current_week, format_date_range, get_latest_sunday, get_previous_sunday from clients import jellyfin @@ -13,6 +15,34 @@ from clients import jellyfin import objc from AppKit import * +CONFIG_PATH = str(Path.home() / ".media_episodes_viewer_config.json") + + +def load_config(): + try: + with open(CONFIG_PATH, "r") as f: + return json.load(f) + except Exception: + return {} + + +def save_config(cfg: dict) -> bool: + try: + with open(CONFIG_PATH, "w") as f: + json.dump(cfg, f) + # update process environment so jellyfin client picks values up + if cfg.get("host"): + os.environ["JELLYFIN_HOST"] = cfg.get("host").rstrip("/") + if cfg.get("key"): + os.environ["JELLYFIN_API_KEY"] = cfg.get("key") + if cfg.get("user"): + os.environ["JELLYFIN_USER_ID"] = cfg.get("user") + if cfg.get("total_episodes"): + os.environ["TOTAL_EPISODES"] = str(cfg.get("total_episodes")) + return True + except Exception: + return False + class AppDelegate(NSObject): def applicationDidFinishLaunching_(self, notification): @@ -42,6 +72,13 @@ class AppDelegate(NSObject): gen_btn.setAction_("generateReport:") content.addSubview_(gen_btn) + # Preferences button + prefs_btn = NSButton.alloc().initWithFrame_(NSMakeRect(620, 610, 120, 30)) + prefs_btn.setTitle_("Preferences...") + prefs_btn.setTarget_(self) + prefs_btn.setAction_("openPreferences:") + content.addSubview_(prefs_btn) + # Report label and box report_label = NSTextField.alloc().initWithFrame_(NSMakeRect(20, 580, 200, 18)) report_label.setStringValue_("Report:") @@ -169,6 +206,128 @@ class AppDelegate(NSObject): alert.setInformativeText_(str(e)) alert.runModal() + def openPreferences_(self, sender): + try: + prefs_rect = NSMakeRect(0.0, 0.0, 480.0, 220.0) + self.prefs_window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(prefs_rect, (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable), NSBackingStoreBuffered, False) + self.prefs_window.center() + self.prefs_window.setTitle_("Preferences") + content = self.prefs_window.contentView() + + # Host + host_label = NSTextField.alloc().initWithFrame_(NSMakeRect(20, 170, 120, 18)) + host_label.setStringValue_("Jellyfin Host:") + host_label.setBezeled_(False) + host_label.setDrawsBackground_(False) + host_label.setEditable_(False) + host_label.setSelectable_(False) + content.addSubview_(host_label) + + host_field = NSTextField.alloc().initWithFrame_(NSMakeRect(150, 165, 300, 24)) + host_field.setStringValue_(os.environ.get("JELLYFIN_HOST", "")) + content.addSubview_(host_field) + self.prefs_host = host_field + + # API Key + api_label = NSTextField.alloc().initWithFrame_(NSMakeRect(20, 130, 120, 18)) + api_label.setStringValue_("API Key:") + api_label.setBezeled_(False) + api_label.setDrawsBackground_(False) + api_label.setEditable_(False) + api_label.setSelectable_(False) + content.addSubview_(api_label) + + api_field = NSTextField.alloc().initWithFrame_(NSMakeRect(150, 125, 300, 24)) + api_field.setStringValue_(os.environ.get("JELLYFIN_API_KEY", "")) + content.addSubview_(api_field) + self.prefs_api_key = api_field + + # User ID + user_label = NSTextField.alloc().initWithFrame_(NSMakeRect(20, 90, 120, 18)) + user_label.setStringValue_("User ID:") + user_label.setBezeled_(False) + user_label.setDrawsBackground_(False) + user_label.setEditable_(False) + user_label.setSelectable_(False) + content.addSubview_(user_label) + + user_field = NSTextField.alloc().initWithFrame_(NSMakeRect(150, 85, 300, 24)) + user_field.setStringValue_(os.environ.get("JELLYFIN_USER_ID", "")) + content.addSubview_(user_field) + self.prefs_user_id = user_field + + # Total episodes + total_label = NSTextField.alloc().initWithFrame_(NSMakeRect(20, 50, 120, 18)) + total_label.setStringValue_("Total Episodes:") + total_label.setBezeled_(False) + total_label.setDrawsBackground_(False) + total_label.setEditable_(False) + total_label.setSelectable_(False) + content.addSubview_(total_label) + + total_field = NSTextField.alloc().initWithFrame_(NSMakeRect(150, 45, 100, 24)) + total_field.setStringValue_(os.environ.get("TOTAL_EPISODES", "1155")) + content.addSubview_(total_field) + self.prefs_total_episodes = total_field + + # Save button + save_btn = NSButton.alloc().initWithFrame_(NSMakeRect(340, 10, 120, 30)) + save_btn.setTitle_("Save") + save_btn.setTarget_(self) + save_btn.setAction_("savePreferences:") + content.addSubview_(save_btn) + + self.prefs_window.makeKeyAndOrderFront_(None) + NSApp.activateIgnoringOtherApps_(True) + except Exception as e: + alert = NSAlert.alloc().init() + alert.setMessageText_("Prefs failed") + alert.setInformativeText_(str(e)) + alert.runModal() + + def savePreferences_(self, sender): + try: + host = (self.prefs_host.string() or "").strip() + key = (self.prefs_api_key.string() or "").strip() + user = (self.prefs_user_id.string() or "").strip() + total = (self.prefs_total_episodes.string() or "").strip() + cfg = {} + if host: + cfg["host"] = host + if key: + cfg["key"] = key + if user: + cfg["user"] = user + if total: + try: + cfg["total_episodes"] = int(total) + except Exception: + pass + ok = save_config(cfg) + if ok: + alert = NSAlert.alloc().init() + alert.setMessageText_("Saved") + alert.setInformativeText_("Preferences saved.") + alert.runModal() + try: + self.debug_view.setString_(f"Saved config: host={cfg.get('host')}, user={cfg.get('user')}, total={cfg.get('total_episodes')}\n" + (self.debug_view.string() or "")) + except Exception: + pass + try: + self.prefs_window.close() + except Exception: + pass + else: + alert = NSAlert.alloc().init() + alert.setMessageText_("Save failed") + alert.setInformativeText_("Could not write config file") + alert.runModal() + except Exception as e: + alert = NSAlert.alloc().init() + alert.setMessageText_("Save failed") + alert.setInformativeText_(str(e)) + alert.runModal() + def main(): app = NSApplication.sharedApplication()