linux observer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix tray stats, remove show-captures, reorganize menu

Replace broken GetStats() D-Bus call with direct filesystem stats computation (60s throttle)

Delete "show captures" menu item and _open_captures() method

Move "open journal" into settings submenu; reorder menu to: status > pause/resume > settings > about > quit

+48 -27
+47 -26
src/solstone_linux/tray.py
··· 12 12 import subprocess 13 13 import time 14 14 from pathlib import Path 15 + from datetime import datetime 15 16 16 17 from dbus_next.aio import MessageBus 17 18 ··· 60 61 self.error = "" 61 62 self.paused_remaining = 0 62 63 self.stats = {} 64 + self._last_stats_time = 0.0 63 65 64 66 # Menu item references for dynamic updates 65 67 self._status_item: MenuItem = None ··· 151 153 else: 152 154 pause_remaining = max(0, int(obs._pause_until - time.monotonic())) 153 155 154 - # Get stats 155 - if obs._dbus_service: 156 + # Compute stats (throttled — filesystem walk every 60s) 157 + now = time.monotonic() 158 + if now - self._last_stats_time >= 60: 159 + self._last_stats_time = now 160 + captures_today = 0 161 + total_size = 0 162 + today = datetime.now().strftime("%Y%m%d") 163 + captures_dir = obs.config.captures_dir 164 + 156 165 try: 157 - raw_stats = obs._dbus_service.GetStats() 158 - self.stats = {k: v.value for k, v in raw_stats.items()} 159 - except Exception: 166 + if captures_dir.exists(): 167 + for day_dir in captures_dir.iterdir(): 168 + if not day_dir.is_dir(): 169 + continue 170 + for stream_dir in day_dir.iterdir(): 171 + if not stream_dir.is_dir(): 172 + continue 173 + for seg_dir in stream_dir.iterdir(): 174 + if not seg_dir.is_dir(): 175 + continue 176 + if seg_dir.name.endswith(".incomplete"): 177 + continue 178 + if seg_dir.name.endswith(".failed"): 179 + continue 180 + if day_dir.name == today: 181 + captures_today += 1 182 + for file_path in seg_dir.iterdir(): 183 + if file_path.is_file(): 184 + total_size += file_path.stat().st_size 185 + except OSError: 160 186 pass 161 187 188 + synced_days = 0 189 + if obs._sync: 190 + synced_days = len(obs._sync._synced_days) 191 + 192 + total_size_mb = int(total_size / (1024 * 1024)) 193 + uptime_seconds = int(time.monotonic() - obs.start_at_mono) 194 + 195 + self.stats = { 196 + "captures_today": captures_today, 197 + "total_size_mb": total_size_mb, 198 + "synced_days": synced_days, 199 + "uptime_seconds": uptime_seconds, 200 + } 201 + 162 202 self._update_status(status) 163 203 self._update_sync(sync_status, sync_progress) 164 204 self._update_live_stats(segment_timer, pause_remaining) ··· 211 251 open_journal = MenuItem( 212 252 label="open journal", 213 253 callback=self._open_journal, 214 - ) 215 - 216 - open_captures = MenuItem( 217 - label="show captures", 218 - callback=self._open_captures, 219 254 ) 220 255 221 256 # ── Settings submenu ── ··· 233 268 children_display="submenu", 234 269 ) 235 270 settings_submenu.children = [ 271 + open_journal, 236 272 settings_open_config, 237 273 settings_copy_agent, 238 274 ] ··· 282 318 self.menu.set_menu( 283 319 [ 284 320 status_submenu, 285 - separator(), 286 - open_journal, 287 - open_captures, 288 321 separator(), 289 322 self._pause_submenu, 290 323 self._resume_item, ··· 378 411 self._segment_item.label = f"segment: {mins}:{secs:02d} remaining" 379 412 self.menu.update_item(self._segment_item) 380 413 381 - # Stats from GetStats 414 + # Stats (computed in update()) 382 415 if self.stats: 383 416 captures = self.stats.get("captures_today", 0) 384 417 size_mb = self.stats.get("total_size_mb", 0) ··· 439 472 def _open_journal(self): 440 473 log.info("Opening journal") 441 474 self._open_url(self.config.server_url or "https://journal.solstone.app") 442 - 443 - def _open_captures(self): 444 - capture_dir = str(self.config.captures_dir) 445 - log.info(f"Opening captures: {capture_dir}") 446 - try: 447 - subprocess.Popen( 448 - ["xdg-open", capture_dir], 449 - stdout=subprocess.DEVNULL, 450 - stderr=subprocess.DEVNULL, 451 - ) 452 - except Exception as e: 453 - log.error(f"Failed to open file manager: {e}") 454 475 455 476 def _open_config(self): 456 477 config_path = str(self.config.config_path)
+1 -1
tests/test_tray.py
··· 54 54 assert len(app._pause_submenu.children) == 4 55 55 assert app._resume_item.visible is False 56 56 assert app.menu._root.children[1].item_type == separator().item_type 57 - assert len(app.menu._root.children) == 12 57 + assert len(app.menu._root.children) == 9 58 58 59 59 60 60 class TestUpdateStatus: