diff --git a/Cargo.lock b/Cargo.lock
index 0bd25ea..6e670a1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3265,6 +3265,7 @@ dependencies = [
name = "trx-wefax"
version = "0.1.0"
dependencies = [
+ "base64",
"png",
"trx-core",
]
diff --git a/src/decoders/trx-wefax/Cargo.toml b/src/decoders/trx-wefax/Cargo.toml
index 1db01f4..02b3741 100644
--- a/src/decoders/trx-wefax/Cargo.toml
+++ b/src/decoders/trx-wefax/Cargo.toml
@@ -9,4 +9,5 @@ edition = "2021"
[dependencies]
trx-core = { path = "../../trx-core" }
+base64 = "0.22"
png = "0.17"
diff --git a/src/decoders/trx-wefax/src/decoder.rs b/src/decoders/trx-wefax/src/decoder.rs
index 9fcff8c..890eebc 100644
--- a/src/decoders/trx-wefax/src/decoder.rs
+++ b/src/decoders/trx-wefax/src/decoder.rs
@@ -9,6 +9,7 @@
use std::path::PathBuf;
+use base64::Engine;
use trx_core::decode::{WefaxMessage, WefaxProgress};
use crate::config::WefaxConfig;
@@ -174,6 +175,8 @@ impl WefaxDecoder {
.last_line()
.map(|l| l.to_vec())
.unwrap_or_default();
+ let b64 = base64::engine::general_purpose::STANDARD
+ .encode(&line_data);
events.push(WefaxEvent::Progress(
WefaxProgress {
rig_id: None,
@@ -181,7 +184,7 @@ impl WefaxDecoder {
lpm,
ioc,
pixels_per_line: WefaxConfig::pixels_per_line(ioc),
- line_data: Some(line_data.clone()),
+ line_data: Some(b64),
},
line_data,
));
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js
index 15f63f1..5594b0f 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js
@@ -82,9 +82,15 @@ function renderGalleryThumbnail(msg) {
var ts = msg._tsMs ? new Date(msg._tsMs).toLocaleString() : '\u2014';
var info = msg.ioc + ' IOC \u00b7 ' + msg.lpm + ' LPM \u00b7 ' + msg.line_count + ' lines';
- if (msg.path) {
+ var imgSrc = msg._dataUrl
+ ? msg._dataUrl
+ : msg.path
+ ? '/images/' + escapeHtml(msg.path.split('/').pop())
+ : null;
+
+ if (imgSrc) {
card.innerHTML =
- '
' +
'
' + escapeHtml(ts) + '
' +
@@ -141,16 +147,19 @@ window.onServerWefaxProgress = function (msg) {
window.onServerWefax = function (msg) {
msg._tsMs = msg.ts_ms || Date.now();
- wefaxImageHistory.unshift(msg);
- pruneWefaxHistory();
- scheduleWefaxGalleryRender();
+ // Capture the live canvas as a data URI for gallery thumbnails.
if (wefaxLiveCtx && wefaxLiveLineCount > 0) {
var trimmed = wefaxLiveCtx.getImageData(0, 0, wefaxLiveCanvas.width, wefaxLiveLineCount);
wefaxLiveCanvas.height = wefaxLiveLineCount;
wefaxLiveCtx.putImageData(trimmed, 0, 0);
+ try { msg._dataUrl = wefaxLiveCanvas.toDataURL('image/png'); } catch (e) {}
}
+ wefaxImageHistory.unshift(msg);
+ pruneWefaxHistory();
+ scheduleWefaxGalleryRender();
+
if (wefaxStatus) {
wefaxStatus.textContent = 'Complete \u2014 ' + msg.line_count + ' lines';
wefaxStatus.style.color = '';
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/api/assets.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/api/assets.rs
index 83e214a..f9f187c 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/src/api/assets.rs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/src/api/assets.rs
@@ -5,6 +5,7 @@
//! Static asset serving endpoints (HTML pages, JS, CSS, favicon, logo).
use actix_web::http::header;
+use actix_web::web;
use actix_web::{get, HttpRequest, HttpResponse, Responder};
use std::sync::OnceLock;
@@ -336,6 +337,30 @@ pub(crate) async fn wefax_js(req: HttpRequest) -> impl Responder {
)
}
+#[get("/images/{filename}")]
+pub(crate) async fn wefax_image(path: web::Path) -> impl Responder {
+ let filename = path.into_inner();
+ // Reject path traversal attempts.
+ if filename.contains('/') || filename.contains('\\') || filename.contains("..") {
+ return HttpResponse::BadRequest().body("invalid filename");
+ }
+ if !filename.ends_with(".png") {
+ return HttpResponse::BadRequest().body("only .png files are accessible");
+ }
+ let dir = dirs::cache_dir()
+ .unwrap_or_else(|| std::path::PathBuf::from(".cache"))
+ .join("trx-rs")
+ .join("wefax");
+ let file_path = dir.join(&filename);
+ match std::fs::read(&file_path) {
+ Ok(data) => HttpResponse::Ok()
+ .insert_header((header::CONTENT_TYPE, "image/png"))
+ .insert_header((header::CACHE_CONTROL, "public, max-age=86400"))
+ .body(data),
+ Err(_) => HttpResponse::NotFound().body("image not found"),
+ }
+}
+
#[get("/bookmarks.js")]
pub(crate) async fn bookmarks_js(req: HttpRequest) -> impl Responder {
let c = gz_bookmarks_js();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs
index ba7697d..c0654c2 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs
@@ -664,6 +664,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
.service(assets::cw_js)
.service(assets::sat_js)
.service(assets::wefax_js)
+ .service(assets::wefax_image)
.service(assets::bookmarks_js)
.service(assets::scheduler_js)
.service(assets::sat_scheduler_js)
diff --git a/src/trx-core/src/decode.rs b/src/trx-core/src/decode.rs
index f10e42d..7a4e15f 100644
--- a/src/trx-core/src/decode.rs
+++ b/src/trx-core/src/decode.rs
@@ -310,5 +310,5 @@ pub struct WefaxProgress {
pub pixels_per_line: u16,
/// Base64-encoded greyscale line data (one row of pixels).
#[serde(skip_serializing_if = "Option::is_none")]
- pub line_data: Option>,
+ pub line_data: Option,
}
diff --git a/src/trx-server/src/audio.rs b/src/trx-server/src/audio.rs
index 1f4dcd6..73fdcb4 100644
--- a/src/trx-server/src/audio.rs
+++ b/src/trx-server/src/audio.rs
@@ -2692,7 +2692,14 @@ pub async fn run_wefax_decoder(
sample_rate, channels
);
- let config = WefaxConfig::default();
+ let wefax_output_dir = dirs::cache_dir()
+ .unwrap_or_else(|| std::path::PathBuf::from(".cache"))
+ .join("trx-rs")
+ .join("wefax");
+ let config = WefaxConfig {
+ output_dir: Some(wefax_output_dir.to_string_lossy().into_owned()),
+ ..WefaxConfig::default()
+ };
let mut decoder = WefaxDecoder::new(sample_rate, config);
let mut was_active = false;
let mut last_reset_seq: u64 = 0;