diff --git a/static/js/modes/sstv-general.js b/static/js/modes/sstv-general.js index c0856e4..7db7ab9 100644 --- a/static/js/modes/sstv-general.js +++ b/static/js/modes/sstv-general.js @@ -345,18 +345,33 @@ const SSTVGeneral = (function() { const liveContent = document.getElementById('sstvGeneralLiveContent'); if (!liveContent) return; - liveContent.innerHTML = ` -
- -
-
-
${data.mode || 'Detecting mode...'}
-
-
+ let container = liveContent.querySelector('.sstv-general-decode-container'); + if (!container) { + liveContent.innerHTML = ` +
+
+ Decoding... +
+
+
+
+
+
+
+
-
${data.message || 'Decoding...'}
-
- `; + `; + container = liveContent.querySelector('.sstv-general-decode-container'); + } + + container.querySelector('.sstv-general-mode-label').textContent = data.mode || 'Detecting mode...'; + container.querySelector('.progress').style.width = (data.progress || 0) + '%'; + container.querySelector('.sstv-general-status-message').textContent = data.message || 'Decoding...'; + + if (data.partial_image) { + const img = container.querySelector('#sstvGeneralDecodeImg'); + if (img) img.src = data.partial_image; + } } /** diff --git a/static/js/modes/sstv.js b/static/js/modes/sstv.js index 227a7b6..44da2f3 100644 --- a/static/js/modes/sstv.js +++ b/static/js/modes/sstv.js @@ -780,18 +780,33 @@ const SSTV = (function() { const liveContent = document.getElementById('sstvLiveContent'); if (!liveContent) return; - liveContent.innerHTML = ` -
- -
-
-
${data.mode || 'Detecting mode...'}
-
-
+ let container = liveContent.querySelector('.sstv-decode-container'); + if (!container) { + liveContent.innerHTML = ` +
+
+ Decoding... +
+
+
+
+
+
+
+
-
${data.message || 'Decoding...'}
-
- `; + `; + container = liveContent.querySelector('.sstv-decode-container'); + } + + container.querySelector('.sstv-mode-label').textContent = data.mode || 'Detecting mode...'; + container.querySelector('.progress').style.width = (data.progress || 0) + '%'; + container.querySelector('.sstv-status-message').textContent = data.message || 'Decoding...'; + + if (data.partial_image) { + const img = container.querySelector('#sstvDecodeImg'); + if (img) img.src = data.partial_image; + } } /** diff --git a/utils/sstv/sstv_decoder.py b/utils/sstv/sstv_decoder.py index f73c639..6b1bc90 100644 --- a/utils/sstv/sstv_decoder.py +++ b/utils/sstv/sstv_decoder.py @@ -9,7 +9,9 @@ original monolithic utils/sstv.py. from __future__ import annotations +import base64 import contextlib +import io import subprocess import threading import time @@ -95,6 +97,7 @@ class DecodeProgress: signal_level: int | None = None # 0-100 RMS audio level, None = not measured sstv_tone: str | None = None # 'leader', 'sync', 'noise', None vis_state: str | None = None # VIS detector state name + partial_image: str | None = None # base64 data URL of partial decode def to_dict(self) -> dict: result: dict = { @@ -114,6 +117,8 @@ class DecodeProgress: result['sstv_tone'] = self.sstv_tone if self.vis_state: result['vis_state'] = self.vis_state + if self.partial_image: + result['partial_image'] = self.partial_image return result @@ -380,6 +385,7 @@ class SSTVDecoder: image_decoder: SSTVImageDecoder | None = None current_mode_name: str | None = None chunk_counter = 0 + last_partial_pct = -1 logger.info("Audio decode thread started") rtl_fm_error: str = '' @@ -418,12 +424,28 @@ class SSTVDecoder: # Currently decoding an image complete = image_decoder.feed(samples) + # Encode partial image every 5% progress + pct = image_decoder.progress_percent + partial_url = None + if pct >= last_partial_pct + 5 or complete: + last_partial_pct = pct + try: + img = image_decoder.get_image() + if img is not None: + buf = io.BytesIO() + img.save(buf, format='JPEG', quality=40) + b64 = base64.b64encode(buf.getvalue()).decode('ascii') + partial_url = f'data:image/jpeg;base64,{b64}' + except Exception: + pass + # Emit progress self._emit_progress(DecodeProgress( status='decoding', mode=current_mode_name, - progress_percent=image_decoder.progress_percent, - message=f'Decoding {current_mode_name}: {image_decoder.progress_percent}%' + progress_percent=pct, + message=f'Decoding {current_mode_name}: {pct}%', + partial_image=partial_url, )) if complete: