Skip to content

Commit 81b841f

Browse files
committed
Reduce cognitive complexity in WebRTC + REST hot paths (S3776)
Each of the flagged ``S3776`` functions was split into smaller named helpers — same behaviour, smaller per-function complexity. No public API changed; tests still pass headless + WS + TLS. webrtc_host.py: - _handle_ctrl_message: dispatch dict + per-message handlers (_handle_input_message / _handle_send_sas_message / _handle_annotate / _handle_renegotiate_answer) plus a shared _safe_audit_log so the rate-limit logging isn't duplicated. - _async_apply_renegotiate_answer: split track re-subscription into _maybe_resubscribe_viewer_video / _audio with a shared _receiver_track helper. - _handle_auth: split into _reject_auth + _auto_approve_via_trust / _whitelist with early-return semantics. - _snapshot_remote_ip: extract _extract_remote_ip so the nested candidate-pair walk reads top-down. webrtc_viewer.py: - _handle_ctrl_message: dispatch dict and per-handler methods matching the host side; _handle_inbox_op_result covers the two inbox response variants. webrtc_stats.py: - _sample: factor candidate-pair / remote-inbound-rtp branches into _absorb_entry / _absorb_remote_inbound. adaptive_bitrate.py: - on_stats: split into _react_to_hard_cap and _react_to_quality; downscale / upscale logic each got their own helper plus _should_downscale / _should_upscale predicates. hw_codec.py: - install_hardware_codec: extract _open_codec_context (codec init + libx264 fallback) and _shape_changed; the patched closure now reads as a thin coordinator. webrtc_dialogs.py: - _refresh: extract _populate_row + _is_stale. - _on_import: extract _prompt_import_data, _import_one, _extract_fingerprints, _confirm_overwrite. webrtc_panel.py: - _read_webrtc_config: introduce _checked_or and _read_region helpers to eliminate the long hasattr ladder. - _on_sessions_context_menu: split into _trust_session_viewer + _copy_session_id_to_clipboard. webrtc_workers.py: - HostPublishLoopWorker.run: extract _publish_one_session, _handle_signaling_error, _safe_stop_session_if so the run loop reads as a one-line state machine. host_service.py: - main: replace the long if/elif chain on args.command with a module-level _COMMAND_DISPATCH dict and per-command helper functions. address_book.py: - upsert: extract _find_entry_locked, _refresh_entry_locked, _build_entry. The early return / merge / append flow now reads linearly. web_viewer/index.html: - handleControlMessage: dispatch object + handleAuthOk / verifyFingerprint / rememberFingerprint helpers replace the if/else-if cascade. - setLanguage: replace nested ternary with explicit if/elif chain.
1 parent c2be066 commit 81b841f

11 files changed

Lines changed: 729 additions & 523 deletions

File tree

je_auto_control/gui/remote_desktop/webrtc_dialogs.py

Lines changed: 83 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -366,28 +366,37 @@ def _refresh(self) -> None:
366366
entries = self._known.list_entries()
367367
self._table.setRowCount(len(entries))
368368
for row, (host_id, fps) in enumerate(sorted(entries.items())):
369-
items = [
370-
QTableWidgetItem(host_id),
371-
QTableWidgetItem(_short_fp(fps.get("app_fp"))),
372-
QTableWidgetItem(_short_fp(fps.get("dtls_fp"))),
373-
QTableWidgetItem(_format_last_seen(fps.get("last_seen"))),
374-
]
375-
is_stale = False
376-
last_seen = fps.get("last_seen")
377-
if last_seen:
378-
try:
379-
dt = datetime.fromisoformat(last_seen)
380-
if now - dt > stale_after:
381-
is_stale = True
382-
except (TypeError, ValueError):
383-
pass
384-
if is_stale:
385-
tip = _t("rd_webrtc_kh_stale_tip")
386-
for it in items:
387-
it.setForeground(stale_color)
388-
it.setToolTip(tip)
389-
for col, item in enumerate(items):
390-
self._table.setItem(row, col, item)
369+
self._populate_row(row, host_id, fps,
370+
now=now, stale_after=stale_after,
371+
stale_color=stale_color)
372+
373+
def _populate_row(self, row: int, host_id: str, fps: dict, *,
374+
now, stale_after, stale_color) -> None:
375+
items = [
376+
QTableWidgetItem(host_id),
377+
QTableWidgetItem(_short_fp(fps.get("app_fp"))),
378+
QTableWidgetItem(_short_fp(fps.get("dtls_fp"))),
379+
QTableWidgetItem(_format_last_seen(fps.get("last_seen"))),
380+
]
381+
if self._is_stale(fps.get("last_seen"), now=now,
382+
stale_after=stale_after):
383+
tip = _t("rd_webrtc_kh_stale_tip")
384+
for it in items:
385+
it.setForeground(stale_color)
386+
it.setToolTip(tip)
387+
for col, item in enumerate(items):
388+
self._table.setItem(row, col, item)
389+
390+
@staticmethod
391+
def _is_stale(last_seen, *, now, stale_after) -> bool:
392+
if not last_seen:
393+
return False
394+
from datetime import datetime
395+
try:
396+
dt = datetime.fromisoformat(last_seen)
397+
except (TypeError, ValueError):
398+
return False
399+
return now - dt > stale_after
391400

392401
def _on_forget(self) -> None:
393402
rows = sorted(
@@ -454,57 +463,74 @@ def _on_export(self) -> None:
454463
QMessageBox.warning(self, "WebRTC", str(error))
455464

456465
def _on_import(self) -> None:
466+
data = self._prompt_import_data()
467+
if data is None:
468+
return
469+
existing = self._known.list_entries()
470+
added = 0
471+
skipped = 0
472+
for host_id, value in data.items():
473+
outcome = self._import_one(host_id, value, existing)
474+
if outcome == "added":
475+
added += 1
476+
elif outcome == "skipped":
477+
skipped += 1
478+
QMessageBox.information(
479+
self, "WebRTC",
480+
_t("rd_webrtc_kh_import_done").format(added=added, skipped=skipped),
481+
)
482+
self._refresh()
483+
484+
def _prompt_import_data(self):
457485
import json
458486
path, _filter = QFileDialog.getOpenFileName(
459487
self, _t("rd_webrtc_kh_import"), "", "JSON (*.json);;All (*)",
460488
)
461489
if not path:
462-
return
490+
return None
463491
try:
464492
with open(path, "r", encoding="utf-8") as fh:
465493
data = json.load(fh)
466494
except (OSError, json.JSONDecodeError) as error:
467495
QMessageBox.warning(self, "WebRTC", str(error))
468-
return
496+
return None
469497
if not isinstance(data, dict):
470498
QMessageBox.warning(
471499
self, "WebRTC", _t("rd_webrtc_kh_import_bad"),
472500
)
473-
return
474-
existing = self._known.list_entries()
475-
added = 0
476-
skipped = 0
477-
for host_id, value in data.items():
478-
if not isinstance(host_id, str):
479-
continue
480-
app_fp = None
481-
dtls_fp = None
482-
if isinstance(value, str):
483-
app_fp = value
484-
elif isinstance(value, dict):
485-
app_fp = value.get("app_fp")
486-
dtls_fp = value.get("dtls_fp")
487-
else:
488-
continue
489-
if host_id in existing:
490-
result = QMessageBox.question(
491-
self, "WebRTC",
492-
_t("rd_webrtc_kh_import_overwrite").format(host=host_id),
493-
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
494-
)
495-
if result != QMessageBox.StandardButton.Yes:
496-
skipped += 1
497-
continue
498-
if isinstance(app_fp, str) and app_fp:
499-
self._known.remember(host_id, app_fp)
500-
if isinstance(dtls_fp, str) and dtls_fp:
501-
self._known.remember_dtls_fingerprint(host_id, dtls_fp)
502-
added += 1
503-
QMessageBox.information(
501+
return None
502+
return data
503+
504+
def _import_one(self, host_id, value, existing) -> str:
505+
"""Return ``"added"``, ``"skipped"``, or ``"ignored"`` per entry."""
506+
if not isinstance(host_id, str):
507+
return "ignored"
508+
app_fp, dtls_fp = self._extract_fingerprints(value)
509+
if app_fp is None and dtls_fp is None:
510+
return "ignored"
511+
if host_id in existing and not self._confirm_overwrite(host_id):
512+
return "skipped"
513+
if isinstance(app_fp, str) and app_fp:
514+
self._known.remember(host_id, app_fp)
515+
if isinstance(dtls_fp, str) and dtls_fp:
516+
self._known.remember_dtls_fingerprint(host_id, dtls_fp)
517+
return "added"
518+
519+
@staticmethod
520+
def _extract_fingerprints(value):
521+
if isinstance(value, str):
522+
return value, None
523+
if isinstance(value, dict):
524+
return value.get("app_fp"), value.get("dtls_fp")
525+
return None, None
526+
527+
def _confirm_overwrite(self, host_id: str) -> bool:
528+
result = QMessageBox.question(
504529
self, "WebRTC",
505-
_t("rd_webrtc_kh_import_done").format(added=added, skipped=skipped),
530+
_t("rd_webrtc_kh_import_overwrite").format(host=host_id),
531+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
506532
)
507-
self._refresh()
533+
return result == QMessageBox.StandardButton.Yes
508534

509535
def _on_forget_stale(self) -> None:
510536
from datetime import datetime, timedelta, timezone

je_auto_control/gui/remote_desktop/webrtc_panel.py

Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -148,73 +148,59 @@ def _build_advanced_group(panel: TranslatableMixin,
148148
return group
149149

150150

151+
def _checked_or(panel, attr: str, default: bool = False) -> bool:
152+
"""Return ``panel.<attr>.isChecked()`` if the widget exists, else default."""
153+
widget = getattr(panel, attr, None)
154+
return widget.isChecked() if widget is not None else default
155+
156+
157+
def _read_region(panel) -> Optional[tuple]:
158+
edit = getattr(panel, "_region_edit", None)
159+
if edit is None:
160+
return None
161+
text = edit.text().strip()
162+
if not text:
163+
return None
164+
try:
165+
parts = [int(p.strip()) for p in text.split(",")]
166+
except (ValueError, TypeError):
167+
return None
168+
return tuple(parts) if len(parts) == 4 else None
169+
170+
151171
def _read_webrtc_config(panel) -> WebRTCConfig:
152172
"""Build a WebRTCConfig from the advanced group + monitor/fps fields."""
153173
from je_auto_control.utils.remote_desktop.webrtc_transport import (
154174
_DEFAULT_STUN_SERVERS,
155175
)
156176
stun_field = panel._stun_edit.text().strip()
157-
if stun_field:
158-
ice_servers = [stun_field]
159-
else:
160-
ice_servers = list(_DEFAULT_STUN_SERVERS)
161-
turn_url = panel._turn_edit.text().strip() or None
162-
show_cursor = (panel._cursor_check.isChecked()
163-
if hasattr(panel, "_cursor_check") else True)
164-
accept_viewer_video = (
165-
panel._accept_viewer_video_check.isChecked()
166-
if hasattr(panel, "_accept_viewer_video_check") else False
167-
)
168-
accept_opus_audio = (
169-
panel._accept_opus_audio_check.isChecked()
170-
if hasattr(panel, "_accept_opus_audio_check") else False
171-
)
172-
share_my_screen = (
173-
panel._share_my_screen_check.isChecked()
174-
if hasattr(panel, "_share_my_screen_check") else False
175-
)
176-
share_opus_mic = (
177-
panel._share_opus_mic_check.isChecked()
178-
if hasattr(panel, "_share_opus_mic_check") else False
177+
ice_servers = [stun_field] if stun_field else list(_DEFAULT_STUN_SERVERS)
178+
monitor = (
179+
int(panel._monitor_combo.currentData() or _DEFAULT_MONITOR)
180+
if hasattr(panel, "_monitor_combo") else _DEFAULT_MONITOR
179181
)
182+
fps = (int(panel._fps_spin.value())
183+
if hasattr(panel, "_fps_spin") else _DEFAULT_FPS)
180184
max_bitrate = (
181185
int(panel._max_bitrate_spin.value())
182186
if hasattr(panel, "_max_bitrate_spin") else 0
183187
)
184-
region = None
185-
if hasattr(panel, "_region_edit"):
186-
text = panel._region_edit.text().strip()
187-
if text:
188-
try:
189-
parts = [int(p.strip()) for p in text.split(",")]
190-
if len(parts) == 4:
191-
region = tuple(parts)
192-
except (ValueError, TypeError):
193-
region = None
194-
config = WebRTCConfig(
188+
return WebRTCConfig(
195189
ice_servers=ice_servers,
196-
turn_url=turn_url,
190+
turn_url=panel._turn_edit.text().strip() or None,
197191
turn_username=panel._turn_user_edit.text().strip() or None,
198192
turn_credential=panel._turn_cred_edit.text() or None,
199-
monitor_index=(
200-
int(panel._monitor_combo.currentData() or _DEFAULT_MONITOR)
201-
if hasattr(panel, "_monitor_combo") else _DEFAULT_MONITOR
202-
),
203-
fps=int(panel._fps_spin.value()) if hasattr(panel, "_fps_spin")
204-
else _DEFAULT_FPS,
205-
show_cursor=show_cursor,
206-
accept_viewer_video=accept_viewer_video,
207-
accept_viewer_audio_opus=accept_opus_audio,
208-
share_my_screen=share_my_screen,
209-
share_my_audio_opus=share_opus_mic,
193+
monitor_index=monitor,
194+
fps=fps,
195+
show_cursor=_checked_or(panel, "_cursor_check", default=True),
196+
accept_viewer_video=_checked_or(panel, "_accept_viewer_video_check"),
197+
accept_viewer_audio_opus=_checked_or(panel, "_accept_opus_audio_check"),
198+
share_my_screen=_checked_or(panel, "_share_my_screen_check"),
199+
share_my_audio_opus=_checked_or(panel, "_share_opus_mic_check"),
210200
max_bitrate_kbps=max_bitrate,
211-
region=region,
212-
host_voice=(
213-
panel._host_voice_check.isChecked()
214-
if hasattr(panel, "_host_voice_check") else False
215-
),
201+
region=_read_region(panel),
202+
host_voice=_checked_or(panel, "_host_voice_check"),
216203
)
217-
return config
218204

219205

220206
class _WebRTCHostPanel(TranslatableMixin, QWidget):
@@ -995,7 +981,7 @@ def _refresh_sessions_table(self) -> None:
995981
self._sessions_table.setItem(row, 4, QTableWidgetItem(connected))
996982

997983
def _on_sessions_context_menu(self, position) -> None:
998-
from PySide6.QtWidgets import QApplication, QMenu
984+
from PySide6.QtWidgets import QMenu
999985
if self._multi_host is None:
1000986
return
1001987
row = self._sessions_table.rowAt(position.y())
@@ -1019,20 +1005,27 @@ def _on_sessions_context_menu(self, position) -> None:
10191005
if chosen is disc:
10201006
self._on_disconnect_selected()
10211007
elif chosen is trust and viewer_id:
1022-
try:
1023-
# find the actual full viewer_id from the host
1024-
with self._multi_host._lock:
1025-
host = self._multi_host._sessions.get(sid)
1026-
full_vid = host.pending_viewer_id if host is not None else None
1027-
if full_vid:
1028-
self._trust_list.add(full_vid, label=f"sess {sid[:6]}")
1029-
self._refresh_trusted_list()
1030-
except (RuntimeError, OSError, ValueError) as error:
1031-
autocontrol_logger.warning("trust viewer: %r", error)
1008+
self._trust_session_viewer(sid)
10321009
elif chosen is copy_id and sid:
1033-
clip = QApplication.clipboard()
1034-
if clip is not None:
1035-
clip.setText(sid)
1010+
self._copy_session_id_to_clipboard(sid)
1011+
1012+
def _trust_session_viewer(self, sid: str) -> None:
1013+
try:
1014+
with self._multi_host._lock:
1015+
host = self._multi_host._sessions.get(sid)
1016+
full_vid = host.pending_viewer_id if host is not None else None
1017+
if full_vid:
1018+
self._trust_list.add(full_vid, label=f"sess {sid[:6]}")
1019+
self._refresh_trusted_list()
1020+
except (RuntimeError, OSError, ValueError) as error:
1021+
autocontrol_logger.warning("trust viewer: %r", error)
1022+
1023+
@staticmethod
1024+
def _copy_session_id_to_clipboard(sid: str) -> None:
1025+
from PySide6.QtWidgets import QApplication
1026+
clip = QApplication.clipboard()
1027+
if clip is not None:
1028+
clip.setText(sid)
10361029

10371030
def _on_disconnect_selected(self) -> None:
10381031
if self._multi_host is None:

0 commit comments

Comments
 (0)