Add Preferences UI and config persistence in mac_cocoa.py

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Pat TeruelandCopilot committed 2026-03-01 11:03:56 +08:00
1 parent 12c168bbfe
commit 46470ced11
1 file changed
+159
+159
View File
@@ -6,6 +6,8 @@ This is a minimal native UI: Generate Report, Copy to Clipboard, and Debug outpu
import os import os
import threading import threading
import json
from pathlib import Path
from datetime import datetime from datetime import datetime
from app import current_week, format_date_range, get_latest_sunday, get_previous_sunday from app import current_week, format_date_range, get_latest_sunday, get_previous_sunday
from clients import jellyfin from clients import jellyfin
@@ -13,6 +15,34 @@ from clients import jellyfin
import objc import objc
from AppKit import * 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): class AppDelegate(NSObject):
def applicationDidFinishLaunching_(self, notification): def applicationDidFinishLaunching_(self, notification):
@@ -42,6 +72,13 @@ class AppDelegate(NSObject):
gen_btn.setAction_("generateReport:") gen_btn.setAction_("generateReport:")
content.addSubview_(gen_btn) 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 and box
report_label = NSTextField.alloc().initWithFrame_(NSMakeRect(20, 580, 200, 18)) report_label = NSTextField.alloc().initWithFrame_(NSMakeRect(20, 580, 200, 18))
report_label.setStringValue_("Report:") report_label.setStringValue_("Report:")
@@ -169,6 +206,128 @@ class AppDelegate(NSObject):
alert.setInformativeText_(str(e)) alert.setInformativeText_(str(e))
alert.runModal() 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(): def main():
app = NSApplication.sharedApplication() app = NSApplication.sharedApplication()