Merge branch 'files'

This commit is contained in:
Marat Fayzullin 2023-02-22 16:35:45 -05:00
commit 91db9c603a
13 changed files with 167 additions and 10 deletions

33
htdocs/css/files.css Normal file
View File

@ -0,0 +1,33 @@
@import url("openwebrx-header.css");
@import url("openwebrx-globals.css");
html, body {
height: unset;
}
body {
margin-bottom: 5rem;
}
hr {
background: #444;
}
h1 {
margin: 1em 0;
text-align: center;
}
table {
border-collapse: separate;
border-spacing: 15px;
}
td {
text-align: center;
border: 3px dotted;
}
img {
width: 100%;
}

View File

@ -158,8 +158,8 @@ input[type=range]:disabled {
{
height: 100%;
position: relative;
display: flex;
flex-direction: column;
display: flex;
flex-direction: column;
}
#openwebrx-scale-container

17
htdocs/files.html Normal file
View File

@ -0,0 +1,17 @@
<HTML><HEAD>
<TITLE>OpenWebRX+ Received Files</TITLE>
<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico" />
<link rel="stylesheet" href="static/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="static/css/files.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.0/showdown.min.js"></script>
<script src="static/lib/jquery-3.2.1.min.js"></script>
<script src="static/lib/Header.js"></script>
</HEAD><BODY>
${header}
<div class="container">
<h1>OpenWebRX+ Received Files</h1>
<table>
${rows}
</table>
</div>
</BODY></HTML>

View File

@ -11,6 +11,7 @@
<div class="button" data-toggle-panel="openwebrx-panel-log"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-log"></use></svg><br/>Log</div>
<div class="button" data-toggle-panel="openwebrx-panel-receiver"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-receiver"></use></svg><br/>Receiver</div>
<a class="button" href="${document_root}map" target="openwebrx-map"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-map"></use></svg><br/>Map</a>
<a class="button" href="${document_root}files" target="openwebrx-files"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-log"></use></svg><br/>Files</a>
<a class="button" href="${document_root}settings" target="openwebrx-settings"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-settings"></use></svg><br/>Settings</a>
<!-- session timeout and use policy page display -->

View File

@ -297,10 +297,12 @@ SstvMessagePanel.prototype.pushMessage = function(msg) {
// $b.scrollTop($b[0].scrollHeight);
}
else if(msg.width>0 && msg.height>0 && !msg.hasOwnProperty('line')) {
var h = '' + msg.timestamp + ' ' + msg.width + 'x' + msg.height +
' ' + msg.sstvMode + '<br>';
var c = '<canvas width="' + msg.width + '" height="' + msg.height +
'" class="frame"></canvas>';
var h = '<div>' + msg.timestamp + ' ' + msg.width + 'x' + msg.height +
' ' + msg.sstvMode + '</div>';
var c = '<div onclick="saveCanvas(\'' + msg.filename + '\');">' +
'<canvas class="frame" id="' + msg.filename +
'" width="' + msg.width + '" height="' + msg.height +
'"></canvas></div>';
// Append a new canvas
$b.append($('<tr><td class="message">' + h + c + '</td></tr>'));
$b.scrollTop($b[0].scrollHeight);

View File

@ -90,6 +90,26 @@ function toggleRecording() {
}
}
function saveCanvas(canvas) {
// Get canvas by its ID
var c = document.getElementById(canvas);
if (c == null) return;
// Create and click a link to the canvas data URL
var a = document.createElement('a');
a.href = c.toDataURL('image/png');
a.style = 'display: none';
a.download = canvas + ".png";
document.body.appendChild(a);
a.click();
// Get rid of the canvas data URL
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(a.href);
}, 0);
}
function zoomInOneStep() {
zoom_set(zoom_level + 1);
}

View File

@ -1,8 +1,12 @@
from owrx.config import ConfigError
from configparser import ConfigParser
import os
import re
from glob import glob
import logging
logger = logging.getLogger(__name__)
class CoreConfig(object):
defaults = {
@ -46,6 +50,28 @@ class CoreConfig(object):
if not os.access(dir, os.W_OK):
raise ConfigError(key, "{dir} is not writable".format(dir=dir))
# Get complete path to a stored file from its filename by
# adding folder name
def getStoredFilePath(self, filename):
return self.get_temporary_directory() + "/" + filename
# Get list of stored files, sorted in reverse alphabetic order
# (so that newer files appear first)
def getStoredFiles(self):
dir = self.get_temporary_directory()
files = [f for f in os.listdir(dir) if re.match(r"SSTV-[0-9]+-[0-9]+\.bmp", f)]
return sorted(files, reverse=True)
# Delete all stored files except for <keepN> newest ones
def cleanStoredFiles(self, keepN):
files = self.getStoredFiles()
for f in files[keepN:]:
logger.debug("Deleting stored file '%s'." % f)
try:
os.unlink(self.getStoredFilePath(f))
except Exception as exptn:
logger.debug(str(exptn))
def get_web_port(self):
return self.web_port
@ -57,3 +83,4 @@ class CoreConfig(object):
def get_aprs_symbols_path(self):
return self.aprs_symbols_path

View File

@ -162,6 +162,7 @@ defaultConfig = PropertyLayer(
callsign_url="https://www.qrzcq.com/call/{}",
usage_policy_url="policy",
session_timeout=0,
keep_files=20,
decoding_queue_workers=2,
decoding_queue_length=10,
wsjt_decoding_depth=3,

38
owrx/controllers/file.py Normal file
View File

@ -0,0 +1,38 @@
from owrx.controllers.template import WebpageController
from owrx.controllers.assets import AssetsController
from owrx.config.core import CoreConfig
class FileController(AssetsController):
def getFilePath(self, file):
return CoreConfig().getStoredFilePath(file)
class FilesController(WebpageController):
def template_variables(self):
files = CoreConfig().getStoredFiles()
rows = ""
for i in range(len(files)):
# Start a row
if i % 3 == 0:
rows += '<tr>\n'
# Print out individual tiles
rows += ('<td class="file-tile">' +
('<a href="/files/%s" download="%s">' % (files[i], files[i])) +
('<img src="/files/%s" download="%s">' % (files[i], files[i])) +
('<p align="center">%s</p>' % files[i]) +
'</a></td>\n')
# Finish a row
if i % 3 == 2:
rows += '</tr>\n'
# Finish final row
if len(files) > 0 and len(files) % 3 != 0:
rows += '</tr>\n'
variables = super().template_variables()
variables["rows"] = rows
return variables
def indexAction(self):
self.serve_template("files.html", **self.template_variables())

View File

@ -70,18 +70,24 @@ class GeneralSettingsController(SettingsFormController):
NumberInput(
"max_clients",
"Maximum number of clients",
infotext="Number of people who can connect at the same time.",
),
NumberInput(
"keep_files",
"Maximum number of files",
infotext="Number of received images and other files to keep.",
),
NumberInput(
"session_timeout",
"Session timeout",
infotext="User session timeout in seconds (0 to disable timeout).",
infotext="Client session timeout in seconds (0 to disable timeout).",
append="secs",
),
TextInput(
"usage_policy_url",
"Usage policy URL",
infotext="Specifies web page describing receiver usage policy "
+ "and shown when the user session times out.",
+ "and shown when a client session times out.",
),
),
Section(

View File

@ -19,7 +19,8 @@ class ReceiverDetails(PropertyFilter):
"photo_title",
"photo_desc",
"usage_policy_url",
"session_timeout",
"session_timeout",
"keep_files",
)
)

View File

@ -1,6 +1,7 @@
from owrx.controllers.status import StatusController
from owrx.controllers.template import IndexController, MapController, PolicyController
from owrx.controllers.feature import FeatureController
from owrx.controllers.file import FilesController, FileController
from owrx.controllers.assets import OwrxAssetsController, AprsSymbolsController, CompiledAssetsController
from owrx.controllers.websocket import WebSocketController
from owrx.controllers.api import ApiController
@ -96,6 +97,8 @@ class Router(object):
StaticRoute("/map", MapController),
StaticRoute("/policy", PolicyController),
StaticRoute("/features", FeatureController),
StaticRoute("/files", FilesController),
RegexRoute("^/files/(SSTV-[0-9]+-[0-9]+\.bmp)$", FileController),
StaticRoute("/api/features", ApiController),
StaticRoute("/metrics", MetricsController, options={"action": "prometheusAction"}),
StaticRoute("/metrics.json", MetricsController),

View File

@ -1,4 +1,5 @@
from owrx.config.core import CoreConfig
from owrx.config import Config
from csdr.module import ThreadModule
from pycsdr.types import Format
from datetime import datetime
@ -80,7 +81,14 @@ class SstvParser(ThreadModule):
if self.height==0 or self.line<self.height:
logger.debug("Deleting short bitmap file '%s'." % self.fileName)
os.unlink(self.fileName)
except Exception:
else:
# Delete excessive files from storage
logger.debug("Performing storage cleanup...")
pm = Config.get()
CoreConfig().cleanStoredFiles(pm["keep_files"])
except Exception as exptn:
logger.debug(str(exptn))
self.file = None
def newFile(self, fileName):