mirror of
https://github.com/smittix/intercept.git
synced 2026-07-11 11:08:12 -07:00
bug fixes and feature updates
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"version": "2026-01-04_e27bf619",
|
"version": "2026-01-11_fae1348c",
|
||||||
"downloaded": "2026-01-07T14:55:20.680977Z"
|
"downloaded": "2026-01-12T15:55:42.769654Z"
|
||||||
}
|
}
|
||||||
+5
-1
@@ -25,6 +25,7 @@ from utils.validation import (
|
|||||||
from utils.sse import format_sse
|
from utils.sse import format_sse
|
||||||
from utils.process import safe_terminate, register_process
|
from utils.process import safe_terminate, register_process
|
||||||
from utils.sdr import SDRFactory, SDRType, SDRValidationError
|
from utils.sdr import SDRFactory, SDRType, SDRValidationError
|
||||||
|
from utils.dependencies import get_tool_path
|
||||||
|
|
||||||
pager_bp = Blueprint('pager', __name__)
|
pager_bp = Blueprint('pager', __name__)
|
||||||
|
|
||||||
@@ -245,7 +246,10 @@ def start_decoding() -> Response:
|
|||||||
bias_t=bias_t
|
bias_t=bias_t
|
||||||
)
|
)
|
||||||
|
|
||||||
multimon_cmd = ['multimon-ng', '-t', 'raw'] + decoders + ['-f', 'alpha', '-']
|
multimon_path = get_tool_path('multimon-ng')
|
||||||
|
if not multimon_path:
|
||||||
|
return jsonify({'status': 'error', 'message': 'multimon-ng not found'}), 400
|
||||||
|
multimon_cmd = [multimon_path, '-t', 'raw'] + decoders + ['-f', 'alpha', '-']
|
||||||
|
|
||||||
full_cmd = ' '.join(rtl_cmd) + ' | ' + ' '.join(multimon_cmd)
|
full_cmd = ' '.join(rtl_cmd) + ' | ' + ' '.join(multimon_cmd)
|
||||||
logger.info(f"Running: {full_cmd}")
|
logger.info(f"Running: {full_cmd}")
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ install_multimon_ng_from_source_macos() {
|
|||||||
trap 'rm -rf "$tmp_dir"' EXIT
|
trap 'rm -rf "$tmp_dir"' EXIT
|
||||||
|
|
||||||
info "Cloning multimon-ng..."
|
info "Cloning multimon-ng..."
|
||||||
git clone --depth 1 https://github.com/EliasOeworkem/multimon-ng.git "$tmp_dir/multimon-ng" >/dev/null 2>&1 \
|
git clone --depth 1 https://github.com/EliasOewornal/multimon-ng.git "$tmp_dir/multimon-ng" >/dev/null 2>&1 \
|
||||||
|| { fail "Failed to clone multimon-ng"; exit 1; }
|
|| { fail "Failed to clone multimon-ng"; exit 1; }
|
||||||
|
|
||||||
cd "$tmp_dir/multimon-ng"
|
cd "$tmp_dir/multimon-ng"
|
||||||
|
|||||||
+51
-19
@@ -1229,8 +1229,8 @@ header h1 .tagline {
|
|||||||
font-family: 'JetBrains Mono', monospace;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
min-height: 100px;
|
min-height: 400px;
|
||||||
max-height: 250px;
|
max-height: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.output-content::-webkit-scrollbar {
|
.output-content::-webkit-scrollbar {
|
||||||
@@ -1496,20 +1496,18 @@ header h1 .tagline {
|
|||||||
background: var(--accent-cyan);
|
background: var(--accent-cyan);
|
||||||
}
|
}
|
||||||
|
|
||||||
.waterfall-container {
|
/* Waterfall canvases (inside collapsible panels) */
|
||||||
padding: 0 15px;
|
#waterfallCanvas,
|
||||||
margin-bottom: 10px;
|
#sensorWaterfallCanvas {
|
||||||
}
|
|
||||||
|
|
||||||
#waterfallCanvas {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60px;
|
height: 30px;
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
border: 1px solid var(--border-color);
|
border: none;
|
||||||
transition: box-shadow 0.3s ease;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#waterfallCanvas.active {
|
#waterfallCanvas.active,
|
||||||
|
#sensorWaterfallCanvas.active {
|
||||||
border-color: var(--accent-cyan);
|
border-color: var(--accent-cyan);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1637,20 +1635,54 @@ header h1 .tagline {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.waterfall-container {
|
/* Removed - now using sensor-waterfall-panel structure for waterfalls */
|
||||||
position: relative;
|
|
||||||
background: #000;
|
/* Waterfall Panel (used for both pager and 433MHz modes) */
|
||||||
|
.sensor-waterfall-panel {
|
||||||
|
margin: 0 15px 10px 15px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#waterfallCanvas {
|
.sensor-waterfall-header {
|
||||||
width: 100%;
|
display: flex;
|
||||||
height: 200px;
|
align-items: center;
|
||||||
display: block;
|
gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sensor-waterfall-header:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sensor-waterfall-content {
|
||||||
|
background: #000;
|
||||||
|
transition: max-height 0.3s ease, padding 0.3s ease;
|
||||||
|
max-height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sensor-waterfall-panel.collapsed .sensor-waterfall-content {
|
||||||
|
max-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sensor-waterfall-panel.collapsed .sensor-waterfall-header {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Removed duplicate - consolidated above */
|
||||||
|
|
||||||
.waterfall-scale {
|
.waterfall-scale {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
+46
-10
@@ -1530,9 +1530,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Device Intelligence Dashboard (above waterfall for prominence) -->
|
<!-- Device Intelligence Dashboard (above waterfall for prominence) -->
|
||||||
<div class="recon-panel" id="reconPanel">
|
<div class="recon-panel collapsed" id="reconPanel">
|
||||||
<div class="recon-header" onclick="toggleReconCollapse()" style="cursor: pointer;">
|
<div class="recon-header" onclick="toggleReconCollapse()" style="cursor: pointer;">
|
||||||
<h4><span id="reconCollapseIcon">▼</span> Device Intelligence</h4>
|
<h4><span id="reconCollapseIcon">▶</span> Device Intelligence</h4>
|
||||||
<div class="recon-stats">
|
<div class="recon-stats">
|
||||||
<div>TRACKED: <span id="trackedCount">0</span></div>
|
<div>TRACKED: <span id="trackedCount">0</span></div>
|
||||||
<div>NEW: <span id="newDeviceCount">0</span></div>
|
<div>NEW: <span id="newDeviceCount">0</span></div>
|
||||||
@@ -1546,16 +1546,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="waterfall-container">
|
|
||||||
<canvas id="waterfallCanvas" width="800" height="60"></canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="output-content" id="output">
|
<div class="output-content" id="output">
|
||||||
<div class="placeholder" style="color: #888; text-align: center; padding: 50px;">
|
<div class="placeholder" style="color: #888; text-align: center; padding: 50px;">
|
||||||
Configure settings and click "Start Decoding" to begin.
|
Configure settings and click "Start Decoding" to begin.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pager Waterfall (pager mode only, at bottom, collapsible) -->
|
||||||
|
<div class="sensor-waterfall-panel collapsed" id="pagerWaterfallPanel" style="display: none;">
|
||||||
|
<div class="sensor-waterfall-header" onclick="togglePagerWaterfall()" style="cursor: pointer;">
|
||||||
|
<span id="pagerWaterfallIcon">▶</span> Signal Waterfall
|
||||||
|
</div>
|
||||||
|
<div class="sensor-waterfall-content" id="pagerWaterfallContent">
|
||||||
|
<canvas id="waterfallCanvas" width="800" height="30"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sensor Waterfall (433MHz mode only, at bottom, collapsible) -->
|
||||||
|
<div class="sensor-waterfall-panel collapsed" id="sensorWaterfallPanel" style="display: none;">
|
||||||
|
<div class="sensor-waterfall-header" onclick="toggleSensorWaterfall()" style="cursor: pointer;">
|
||||||
|
<span id="sensorWaterfallIcon">▶</span> Signal Waterfall
|
||||||
|
</div>
|
||||||
|
<div class="sensor-waterfall-content" id="sensorWaterfallContent">
|
||||||
|
<canvas id="sensorWaterfallCanvas" width="800" height="30"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="status-bar">
|
<div class="status-bar">
|
||||||
<div class="status-indicator">
|
<div class="status-indicator">
|
||||||
<div class="status-dot" id="statusDot"></div>
|
<div class="status-dot" id="statusDot"></div>
|
||||||
@@ -2231,7 +2247,10 @@
|
|||||||
document.getElementById('toolStatusAircraft').style.display = (mode === 'aircraft') ? 'grid' : 'none';
|
document.getElementById('toolStatusAircraft').style.display = (mode === 'aircraft') ? 'grid' : 'none';
|
||||||
|
|
||||||
// Hide waterfall and output console for modes with their own visualizations
|
// Hide waterfall and output console for modes with their own visualizations
|
||||||
document.querySelector('.waterfall-container').style.display = (mode === 'satellite' || mode === 'listening' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth') ? 'none' : 'block';
|
// Pager waterfall: show only for pager mode
|
||||||
|
document.getElementById('pagerWaterfallPanel').style.display = (mode === 'pager') ? 'block' : 'none';
|
||||||
|
// Sensor waterfall: show only for sensor (433MHz) mode
|
||||||
|
document.getElementById('sensorWaterfallPanel').style.display = (mode === 'sensor') ? 'block' : 'none';
|
||||||
document.getElementById('output').style.display = (mode === 'satellite' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth' || mode === 'listening') ? 'none' : 'block';
|
document.getElementById('output').style.display = (mode === 'satellite' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth' || mode === 'listening') ? 'none' : 'block';
|
||||||
document.querySelector('.status-bar').style.display = (mode === 'satellite') ? 'none' : 'flex';
|
document.querySelector('.status-bar').style.display = (mode === 'satellite') ? 'none' : 'flex';
|
||||||
|
|
||||||
@@ -2518,8 +2537,9 @@
|
|||||||
signalActivity = Math.min(1, signalActivity + 0.4);
|
signalActivity = Math.min(1, signalActivity + 0.4);
|
||||||
lastMessageTime = Date.now();
|
lastMessageTime = Date.now();
|
||||||
|
|
||||||
// Flash waterfall canvas
|
// Flash waterfall canvas (use appropriate canvas based on mode)
|
||||||
const canvas = document.getElementById('waterfallCanvas');
|
const canvasId = (currentMode === 'sensor') ? 'sensorWaterfallCanvas' : 'waterfallCanvas';
|
||||||
|
const canvas = document.getElementById(canvasId);
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
canvas.classList.add('active');
|
canvas.classList.add('active');
|
||||||
setTimeout(() => canvas.classList.remove('active'), 500);
|
setTimeout(() => canvas.classList.remove('active'), 500);
|
||||||
@@ -2539,7 +2559,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderWaterfall() {
|
function renderWaterfall() {
|
||||||
const canvas = document.getElementById('waterfallCanvas');
|
// Render to the appropriate canvas based on current mode
|
||||||
|
const canvasId = (currentMode === 'sensor') ? 'sensorWaterfallCanvas' : 'waterfallCanvas';
|
||||||
|
const canvas = document.getElementById(canvasId);
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
const width = canvas.width;
|
const width = canvas.width;
|
||||||
@@ -3537,6 +3559,20 @@
|
|||||||
icon.textContent = panel.classList.contains('collapsed') ? '▶' : '▼';
|
icon.textContent = panel.classList.contains('collapsed') ? '▶' : '▼';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSensorWaterfall() {
|
||||||
|
const panel = document.getElementById('sensorWaterfallPanel');
|
||||||
|
const icon = document.getElementById('sensorWaterfallIcon');
|
||||||
|
panel.classList.toggle('collapsed');
|
||||||
|
icon.textContent = panel.classList.contains('collapsed') ? '▶' : '▼';
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePagerWaterfall() {
|
||||||
|
const panel = document.getElementById('pagerWaterfallPanel');
|
||||||
|
const icon = document.getElementById('pagerWaterfallIcon');
|
||||||
|
panel.classList.toggle('collapsed');
|
||||||
|
icon.textContent = panel.classList.contains('collapsed') ? '▶' : '▼';
|
||||||
|
}
|
||||||
|
|
||||||
// ============== WIFI RECONNAISSANCE ==============
|
// ============== WIFI RECONNAISSANCE ==============
|
||||||
|
|
||||||
let wifiEventSource = null;
|
let wifiEventSource = null;
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ from typing import Any
|
|||||||
|
|
||||||
logger = logging.getLogger('intercept.dependencies')
|
logger = logging.getLogger('intercept.dependencies')
|
||||||
|
|
||||||
# Additional paths to search for tools (e.g., /usr/sbin on Debian)
|
# Additional paths to search for tools (e.g., /usr/sbin on Debian, /opt/local/bin for MacPorts)
|
||||||
EXTRA_TOOL_PATHS = ['/usr/sbin', '/sbin']
|
EXTRA_TOOL_PATHS = ['/usr/sbin', '/sbin', '/opt/local/bin', '/opt/local/sbin']
|
||||||
|
|
||||||
|
|
||||||
def check_tool(name: str) -> bool:
|
def check_tool(name: str) -> bool:
|
||||||
@@ -51,7 +51,7 @@ TOOL_DEPENDENCIES = {
|
|||||||
'description': 'Digital transmission decoder',
|
'description': 'Digital transmission decoder',
|
||||||
'install': {
|
'install': {
|
||||||
'apt': 'sudo apt install multimon-ng',
|
'apt': 'sudo apt install multimon-ng',
|
||||||
'brew': 'brew install multimon-ng',
|
'brew': 'sudo port install multimon-ng (MacPorts) or build from source',
|
||||||
'manual': 'https://github.com/EliasOewornal/multimon-ng'
|
'manual': 'https://github.com/EliasOewornal/multimon-ng'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+7
-3
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
|
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
|
||||||
|
from utils.dependencies import get_tool_path
|
||||||
|
|
||||||
|
|
||||||
class RTLSDRCommandBuilder(CommandBuilder):
|
class RTLSDRCommandBuilder(CommandBuilder):
|
||||||
@@ -53,8 +54,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
|
|||||||
|
|
||||||
Used for pager decoding. Supports local devices and rtl_tcp connections.
|
Used for pager decoding. Supports local devices and rtl_tcp connections.
|
||||||
"""
|
"""
|
||||||
|
rtl_fm_path = get_tool_path('rtl_fm') or 'rtl_fm'
|
||||||
cmd = [
|
cmd = [
|
||||||
'rtl_fm',
|
rtl_fm_path,
|
||||||
'-d', self._get_device_arg(device),
|
'-d', self._get_device_arg(device),
|
||||||
'-f', f'{frequency_mhz}M',
|
'-f', f'{frequency_mhz}M',
|
||||||
'-M', modulation,
|
'-M', modulation,
|
||||||
@@ -99,8 +101,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
|
|||||||
"connect to its SBS output (port 30003)."
|
"connect to its SBS output (port 30003)."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
dump1090_path = get_tool_path('dump1090') or 'dump1090'
|
||||||
cmd = [
|
cmd = [
|
||||||
'dump1090',
|
dump1090_path,
|
||||||
'--net',
|
'--net',
|
||||||
'--device-index', str(device.index),
|
'--device-index', str(device.index),
|
||||||
'--quiet'
|
'--quiet'
|
||||||
@@ -127,8 +130,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
|
|||||||
|
|
||||||
Outputs JSON for easy parsing. Supports local devices and rtl_tcp connections.
|
Outputs JSON for easy parsing. Supports local devices and rtl_tcp connections.
|
||||||
"""
|
"""
|
||||||
|
rtl_433_path = get_tool_path('rtl_433') or 'rtl_433'
|
||||||
cmd = [
|
cmd = [
|
||||||
'rtl_433',
|
rtl_433_path,
|
||||||
'-d', self._get_device_arg(device),
|
'-d', self._get_device_arg(device),
|
||||||
'-f', f'{frequency_mhz}M',
|
'-f', f'{frequency_mhz}M',
|
||||||
'-F', 'json'
|
'-F', 'json'
|
||||||
|
|||||||
Reference in New Issue
Block a user