pakt 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pakt/tray.py ADDED
@@ -0,0 +1,137 @@
1
+ """System tray integration for Pakt."""
2
+ # pyright: reportPossiblyUnboundVariable=false
3
+
4
+ from __future__ import annotations
5
+
6
+ import threading
7
+ import webbrowser
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING, Callable
10
+
11
+ if TYPE_CHECKING:
12
+ import pystray
13
+ from PIL import Image
14
+
15
+ from pakt.scheduler import SyncScheduler
16
+
17
+ try:
18
+ import pystray
19
+ from PIL import Image
20
+
21
+ TRAY_AVAILABLE = True
22
+ except ImportError:
23
+ TRAY_AVAILABLE = False
24
+
25
+
26
+ def _get_icon_image() -> "Image.Image":
27
+ """Load icon from assets or create fallback."""
28
+ if not TRAY_AVAILABLE:
29
+ raise ImportError("PIL is required")
30
+
31
+ # Try to load from assets
32
+ assets_dir = Path(__file__).parent / "assets"
33
+ icon_path = assets_dir / "icon.png"
34
+ if icon_path.exists():
35
+ return Image.open(icon_path)
36
+
37
+ # Fallback: create simple programmatic icon
38
+ size = 64
39
+ img = Image.new("RGBA", (size, size), (45, 212, 191, 255)) # Teal background
40
+ return img
41
+
42
+
43
+ class PaktTray:
44
+ """System tray icon for Pakt."""
45
+
46
+ def __init__(
47
+ self,
48
+ web_url: str = "http://localhost:8080",
49
+ sync_callback: Callable[[], None] | None = None,
50
+ shutdown_callback: Callable[[], None] | None = None,
51
+ scheduler: "SyncScheduler | None" = None,
52
+ ) -> None:
53
+ """Initialize the system tray.
54
+
55
+ Args:
56
+ web_url: URL of the web interface
57
+ sync_callback: Function to trigger sync
58
+ shutdown_callback: Function to shutdown the application
59
+ scheduler: Scheduler instance for status display
60
+ """
61
+ if not TRAY_AVAILABLE:
62
+ raise ImportError("pystray and Pillow are required for system tray support")
63
+
64
+ self.web_url = web_url
65
+ self.sync_callback = sync_callback
66
+ self.shutdown_callback = shutdown_callback
67
+ self.scheduler = scheduler
68
+ self._icon: pystray.Icon | None = None
69
+ self._thread: threading.Thread | None = None
70
+
71
+ def _open_web_ui(self) -> None:
72
+ """Open the web UI in the default browser."""
73
+ webbrowser.open(self.web_url)
74
+
75
+ def _trigger_sync(self) -> None:
76
+ """Trigger a sync operation."""
77
+ if self.sync_callback:
78
+ self.sync_callback()
79
+
80
+ def _exit(self) -> None:
81
+ """Exit the application."""
82
+ if self._icon:
83
+ self._icon.stop()
84
+ if self.shutdown_callback:
85
+ self.shutdown_callback()
86
+
87
+ def _get_menu(self) -> "pystray.Menu":
88
+ """Create the context menu."""
89
+ items = [
90
+ pystray.MenuItem("Open Web UI", self._open_web_ui, default=True),
91
+ pystray.MenuItem("Sync Now", self._trigger_sync),
92
+ pystray.Menu.SEPARATOR,
93
+ ]
94
+
95
+ # Add next run info if scheduler is available
96
+ if self.scheduler and self.scheduler.is_enabled:
97
+ next_run = self.scheduler.next_run
98
+ if next_run:
99
+ next_str = next_run.strftime("%H:%M")
100
+ items.append(
101
+ pystray.MenuItem(f"Next sync: {next_str}", None, enabled=False)
102
+ )
103
+ items.append(pystray.Menu.SEPARATOR)
104
+
105
+ items.append(pystray.MenuItem("Exit", self._exit))
106
+ return pystray.Menu(*items)
107
+
108
+ def start(self) -> None:
109
+ """Start the system tray icon in a background thread."""
110
+ if not TRAY_AVAILABLE:
111
+ return
112
+
113
+ icon_image = _get_icon_image()
114
+ icon = pystray.Icon(
115
+ name="Pakt",
116
+ icon=icon_image,
117
+ title="Pakt - Plex/Trakt Sync",
118
+ menu=self._get_menu(),
119
+ )
120
+ self._icon = icon
121
+
122
+ def run_icon():
123
+ icon.run()
124
+
125
+ self._thread = threading.Thread(target=run_icon, daemon=True)
126
+ self._thread.start()
127
+
128
+ def stop(self) -> None:
129
+ """Stop the system tray icon."""
130
+ if self._icon:
131
+ self._icon.stop()
132
+ self._icon = None
133
+
134
+ def update_menu(self) -> None:
135
+ """Update the menu (e.g., after scheduler status changes)."""
136
+ if self._icon:
137
+ self._icon.menu = self._get_menu()
pakt/web/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Web interface for Pakt."""
2
+
3
+ from pakt.web.app import create_app
4
+
5
+ __all__ = ["create_app"]