v2.26.0: fix SSE fanout crash and branded logo FOUC

- Fix SSE fanout thread AttributeError when source queue is None during
  interpreter shutdown by snapshotting to local variable with null guard
- Fix branded "i" logo rendering oversized on first page load (FOUC) by
  adding inline width/height to SVG elements across 10 templates
- Bump version to 2.26.0 in config.py, pyproject.toml, and CHANGELOG.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-03-13 11:51:27 +00:00
parent 00362bcd57
commit e00fbfddc1
183 changed files with 2006 additions and 4243 deletions
+6 -6
View File
@@ -25,22 +25,22 @@ from __future__ import annotations
from typing import Optional
from .airspy import AirspyCommandBuilder
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
from .detection import detect_all_devices, invalidate_device_cache, probe_rtlsdr_device
from .rtlsdr import RTLSDRCommandBuilder
from .limesdr import LimeSDRCommandBuilder
from .hackrf import HackRFCommandBuilder
from .airspy import AirspyCommandBuilder
from .limesdr import LimeSDRCommandBuilder
from .rtlsdr import RTLSDRCommandBuilder
from .sdrplay import SDRPlayCommandBuilder
from .validation import (
SDRValidationError,
get_capabilities_for_type,
validate_device_index,
validate_frequency,
validate_gain,
validate_sample_rate,
validate_ppm,
validate_device_index,
validate_sample_rate,
validate_squelch,
get_capabilities_for_type,
)
+9 -11
View File
@@ -8,8 +8,6 @@ Airspy HF+ supports 9 kHz - 31 MHz and 60-260 MHz.
from __future__ import annotations
from typing import Optional
from utils.dependencies import get_tool_path
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
@@ -63,10 +61,10 @@ class AirspyCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 22050,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
modulation: str = "fm",
squelch: Optional[int] = None,
squelch: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -102,7 +100,7 @@ class AirspyCommandBuilder(CommandBuilder):
def build_adsb_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -132,8 +130,8 @@ class AirspyCommandBuilder(CommandBuilder):
self,
device: SDRDevice,
frequency_mhz: float = 433.92,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -161,7 +159,7 @@ class AirspyCommandBuilder(CommandBuilder):
def build_ais_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
) -> list[str]:
@@ -193,8 +191,8 @@ class AirspyCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 2048000,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False,
output_format: str = 'cu8',
) -> list[str]:
+11 -12
View File
@@ -10,7 +10,6 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
class SDRType(Enum):
@@ -49,8 +48,8 @@ class SDRDevice:
serial: str
driver: str # e.g., "rtlsdr", "lime", "hackrf"
capabilities: SDRCapabilities
rtl_tcp_host: Optional[str] = None # Remote rtl_tcp server host
rtl_tcp_port: Optional[int] = None # Remote rtl_tcp server port
rtl_tcp_host: str | None = None # Remote rtl_tcp server host
rtl_tcp_port: int | None = None # Remote rtl_tcp server port
@property
def is_network(self) -> bool:
@@ -92,10 +91,10 @@ class CommandBuilder(ABC):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 22050,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
modulation: str = "fm",
squelch: Optional[int] = None,
squelch: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -120,7 +119,7 @@ class CommandBuilder(ABC):
def build_adsb_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -141,8 +140,8 @@ class CommandBuilder(ABC):
self,
device: SDRDevice,
frequency_mhz: float = 433.92,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -164,7 +163,7 @@ class CommandBuilder(ABC):
def build_ais_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
) -> list[str]:
@@ -192,8 +191,8 @@ class CommandBuilder(ABC):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 2048000,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False,
output_format: str = 'cu8',
) -> list[str]:
+8 -10
View File
@@ -6,11 +6,11 @@ Detects RTL-SDR devices via rtl_test and other SDR hardware via SoapySDR.
from __future__ import annotations
import contextlib
import logging
import re
import subprocess
import time
from typing import Optional
from utils.dependencies import get_tool_path
@@ -47,10 +47,10 @@ def _hackrf_probe_blocked() -> bool:
def _get_capabilities_for_type(sdr_type: SDRType) -> SDRCapabilities:
"""Get default capabilities for an SDR type."""
# Import here to avoid circular imports
from .rtlsdr import RTLSDRCommandBuilder
from .limesdr import LimeSDRCommandBuilder
from .hackrf import HackRFCommandBuilder
from .airspy import AirspyCommandBuilder
from .hackrf import HackRFCommandBuilder
from .limesdr import LimeSDRCommandBuilder
from .rtlsdr import RTLSDRCommandBuilder
from .sdrplay import SDRPlayCommandBuilder
builders = {
@@ -79,7 +79,7 @@ def _get_capabilities_for_type(sdr_type: SDRType) -> SDRCapabilities:
)
def _driver_to_sdr_type(driver: str) -> Optional[SDRType]:
def _driver_to_sdr_type(driver: str) -> SDRType | None:
"""Map SoapySDR driver name to SDRType."""
mapping = {
'rtlsdr': SDRType.RTL_SDR,
@@ -114,7 +114,7 @@ def detect_rtlsdr_devices() -> list[SDRDevice]:
import os
import platform
env = os.environ.copy()
if platform.system() == 'Darwin':
lib_paths = ['/usr/local/lib', '/opt/homebrew/lib']
current_ld = env.get('DYLD_LIBRARY_PATH', '')
@@ -224,7 +224,7 @@ def _get_soapy_env() -> dict:
return env
def detect_soapy_devices(skip_types: Optional[set[SDRType]] = None) -> list[SDRDevice]:
def detect_soapy_devices(skip_types: set[SDRType] | None = None) -> list[SDRDevice]:
"""
Detect SDR devices via SoapySDR.
@@ -497,10 +497,8 @@ def probe_rtlsdr_device(device_index: int) -> str | None:
# rtl_test exited with error and we never saw a success message
error_found = True
finally:
try:
with contextlib.suppress(OSError):
proc.kill()
except OSError:
pass
proc.wait()
if device_found:
# Allow the kernel to fully release the USB interface
+10 -12
View File
@@ -7,8 +7,6 @@ HackRF supports 1 MHz to 6 GHz frequency range.
from __future__ import annotations
from typing import Optional
from utils.dependencies import get_tool_path
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
@@ -33,7 +31,7 @@ class HackRFCommandBuilder(CommandBuilder):
"""Build SoapySDR device string for HackRF."""
if device.serial and device.serial != 'N/A':
return f'driver=hackrf,serial={device.serial}'
return f'driver=hackrf'
return 'driver=hackrf'
def _split_gain(self, gain: float) -> tuple[int, int]:
"""
@@ -59,10 +57,10 @@ class HackRFCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 22050,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
modulation: str = "fm",
squelch: Optional[int] = None,
squelch: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -99,7 +97,7 @@ class HackRFCommandBuilder(CommandBuilder):
def build_adsb_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -129,8 +127,8 @@ class HackRFCommandBuilder(CommandBuilder):
self,
device: SDRDevice,
frequency_mhz: float = 433.92,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -161,7 +159,7 @@ class HackRFCommandBuilder(CommandBuilder):
def build_ais_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
) -> list[str]:
@@ -193,8 +191,8 @@ class HackRFCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 2048000,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False,
output_format: str = 'cu8',
) -> list[str]:
+10 -12
View File
@@ -7,8 +7,6 @@ LimeSDR supports 100 kHz to 3.8 GHz frequency range.
from __future__ import annotations
from typing import Optional
from utils.dependencies import get_tool_path
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
@@ -33,17 +31,17 @@ class LimeSDRCommandBuilder(CommandBuilder):
"""Build SoapySDR device string for LimeSDR."""
if device.serial and device.serial != 'N/A':
return f'driver=lime,serial={device.serial}'
return f'driver=lime'
return 'driver=lime'
def build_fm_demod_command(
self,
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 22050,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
modulation: str = "fm",
squelch: Optional[int] = None,
squelch: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -78,7 +76,7 @@ class LimeSDRCommandBuilder(CommandBuilder):
def build_adsb_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -108,8 +106,8 @@ class LimeSDRCommandBuilder(CommandBuilder):
self,
device: SDRDevice,
frequency_mhz: float = 433.92,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -140,7 +138,7 @@ class LimeSDRCommandBuilder(CommandBuilder):
def build_ais_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
) -> list[str]:
@@ -170,8 +168,8 @@ class LimeSDRCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 2048000,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False,
output_format: str = 'cu8',
) -> list[str]:
+13 -13
View File
@@ -10,10 +10,10 @@ from __future__ import annotations
import logging
import re
import subprocess
from typing import Optional
from utils.dependencies import get_tool_path
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
from utils.dependencies import get_tool_path
logger = logging.getLogger('intercept.sdr.rtlsdr')
@@ -46,7 +46,7 @@ def _rtl_tool_supports_bias_t(tool_path: str) -> bool:
return False
def _get_dump1090_bias_t_flag(dump1090_path: str) -> Optional[str]:
def _get_dump1090_bias_t_flag(dump1090_path: str) -> str | None:
"""Detect the correct bias-t flag for the installed dump1090 variant.
Different dump1090 forks use different flags:
@@ -106,12 +106,12 @@ class RTLSDRCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 22050,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
modulation: str = "fm",
squelch: Optional[int] = None,
squelch: int | None = None,
bias_t: bool = False,
direct_sampling: Optional[int] = None,
direct_sampling: int | None = None,
) -> list[str]:
"""
Build rtl_fm command for FM demodulation.
@@ -163,7 +163,7 @@ class RTLSDRCommandBuilder(CommandBuilder):
def build_adsb_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -208,8 +208,8 @@ class RTLSDRCommandBuilder(CommandBuilder):
self,
device: SDRDevice,
frequency_mhz: float = 433.92,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -247,7 +247,7 @@ class RTLSDRCommandBuilder(CommandBuilder):
def build_ais_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
) -> list[str]:
@@ -283,8 +283,8 @@ class RTLSDRCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 2048000,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False,
output_format: str = 'cu8',
) -> list[str]:
+9 -11
View File
@@ -7,8 +7,6 @@ SDRPlay RSP devices support 1 kHz to 2 GHz frequency range.
from __future__ import annotations
from typing import Optional
from utils.dependencies import get_tool_path
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
@@ -41,10 +39,10 @@ class SDRPlayCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 22050,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
modulation: str = "fm",
squelch: Optional[int] = None,
squelch: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -80,7 +78,7 @@ class SDRPlayCommandBuilder(CommandBuilder):
def build_adsb_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -110,8 +108,8 @@ class SDRPlayCommandBuilder(CommandBuilder):
self,
device: SDRDevice,
frequency_mhz: float = 433.92,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False
) -> list[str]:
"""
@@ -139,7 +137,7 @@ class SDRPlayCommandBuilder(CommandBuilder):
def build_ais_command(
self,
device: SDRDevice,
gain: Optional[float] = None,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
) -> list[str]:
@@ -171,8 +169,8 @@ class SDRPlayCommandBuilder(CommandBuilder):
device: SDRDevice,
frequency_mhz: float,
sample_rate: int = 2048000,
gain: Optional[float] = None,
ppm: Optional[int] = None,
gain: float | None = None,
ppm: int | None = None,
bias_t: bool = False,
output_format: str = 'cu8',
) -> list[str]:
+2 -2
View File
@@ -240,9 +240,9 @@ def get_capabilities_for_type(sdr_type: SDRType) -> SDRCapabilities:
Returns:
SDRCapabilities for the specified type
"""
from .rtlsdr import RTLSDRCommandBuilder
from .limesdr import LimeSDRCommandBuilder
from .hackrf import HackRFCommandBuilder
from .limesdr import LimeSDRCommandBuilder
from .rtlsdr import RTLSDRCommandBuilder
builders = {
SDRType.RTL_SDR: RTLSDRCommandBuilder,