Deal with the recordings, not just read them
A night's scan leaves hundreds of files, most worth nothing and a few of them the reason it was left running. Sorting that out meant leaving the browser and going at the directory with mv and rm. Five keys, meant to be pressed once each going down the list: S I N file it into saved/, investigate/ or noise/ u put the last one filed back d delete it and its sidecars, for good -- asks first m lock the frequency out, so no later scan stops on it Each of these acts on the whole capture -- the .wav, the JSON sidecar, the IQ, the transcript and the decoded data -- because a recording in one directory and its transcript in another is a pair nothing will ever put back together. A move that cannot be finished puts back whatever already moved. The cursor stays on the row it was on, which is now the next recording, since a cursor that jumped would make one-key-per-recording impossible. m writes to the lock-out list in the settings file, the same one the scanner's own l key maintains, so a birdie found while reading last night's recordings is gone from tonight's. It says "the next scan": one already running read its settings when it started. The subdirectories sit under the recordings directory, so a scan writing there never looks in them, and saunterbrowse ~/bandsaunter/saved reads one back. Also here, because this is the first part of the browser that writes: - The help screen is back inside eighty by twenty-four. It had grown past the bottom of an ordinary window, which puts "q quit" off the screen. - The footer drops keys in a deliberate order when the window is narrow, rather than ellipsising whichever happened to be at the end. - Moving or deleting what is playing stops the player first. - The pty harness accepted an env and ignored it, so a test aimed at a throwaway settings directory wrote to the real one. It honours it now, and conftest redirects the settings directory for every test besides. 1014 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
This commit is contained in:
parent
d6ae6d0c22
commit
f9f0d94000
11 changed files with 1162 additions and 44 deletions
|
|
@ -1,5 +1,5 @@
|
|||
.\" Generated by packaging/make-man.py -- do not edit by hand.
|
||||
.TH BANDSAUNTER 1 "2026-08-28" "bandsaunter 2026-08-28_03" "User Commands"
|
||||
.TH BANDSAUNTER 1 "2026-08-29" "bandsaunter 2026-08-29_01" "User Commands"
|
||||
.SH NAME
|
||||
bandsaunter \- scan, record and identify radio signals with an RTL-SDR
|
||||
.SH SYNOPSIS
|
||||
|
|
|
|||
|
|
@ -13,7 +13,15 @@ from pathlib import Path
|
|||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
import bandsaunter # noqa: E402
|
||||
from bandsaunter.browse import PLAYERS, SORTS # noqa: E402
|
||||
from bandsaunter.browse import FILING, PLAYERS, SORTS # noqa: E402
|
||||
|
||||
|
||||
def _english(items) -> str:
|
||||
"""a, b and c -- the way a sentence lists things."""
|
||||
items = list(items)
|
||||
if len(items) < 2:
|
||||
return "".join(items)
|
||||
return ", ".join(items[:-1]) + " and " + items[-1]
|
||||
|
||||
|
||||
PAGE = r'''.\" Generated by packaging/make-browse-man.py -- do not edit by hand.
|
||||
|
|
@ -52,8 +60,10 @@ With no
|
|||
it opens the one the scanner writes to, taken from your saved settings, so it
|
||||
normally needs no arguments at all.
|
||||
.PP
|
||||
It only ever reads. Nothing in the recordings directory is renamed, moved or
|
||||
deleted.
|
||||
Recordings can also be dealt with as they are read. A key files one into
|
||||
{filing_dirs}, another deletes it outright, and another locks its frequency
|
||||
out of every later scan. Nothing else is written: without one of those keys
|
||||
the browser only reads.
|
||||
.SH KEYS
|
||||
.TP
|
||||
.B "Up Down k j"
|
||||
|
|
@ -95,6 +105,27 @@ it, and this picks up what has arrived since.
|
|||
Print the highlighted recording's path and quit, for piping into something
|
||||
else.
|
||||
.TP
|
||||
.B "{filing_keys}"
|
||||
File the highlighted recording into {filing_list} respectively \[em] the
|
||||
recording and every sidecar written beside it, together. See
|
||||
.B MANAGING THE RECORDINGS
|
||||
below.
|
||||
.TP
|
||||
.B u
|
||||
Put the last recording that was filed back where it came from. One level
|
||||
only, and a delete cannot be undone this way.
|
||||
.TP
|
||||
.B d
|
||||
Delete the highlighted recording and its sidecars for good. It asks first:
|
||||
this is the one key here that cannot be taken back.
|
||||
.TP
|
||||
.B m
|
||||
Lock the highlighted recording's frequency out, so that no later scan stops
|
||||
on it again. The frequency is written into your saved settings, the same list
|
||||
.BR bandsaunter (1)
|
||||
maintains, and takes effect on the next scan \[em] a scan already running read
|
||||
its settings when it started.
|
||||
.TP
|
||||
.B "? h"
|
||||
The list of keys, and which audio player was found.
|
||||
.TP
|
||||
|
|
@ -225,6 +256,45 @@ KML is the format Google Earth uses.
|
|||
.BR marble (1)
|
||||
and OsmAnd open it too, and it is XML, so a scan interrupted halfway through
|
||||
leaves a file that still opens.
|
||||
.SH MANAGING THE RECORDINGS
|
||||
A night's scan leaves hundreds of files, most of which are worth nothing and
|
||||
a few of which are the reason you left it running. Deciding which is which is
|
||||
what this program is for, and the keys that act on a recording are meant to
|
||||
be pressed once each, going down the list.
|
||||
{filing_prose}
|
||||
.PP
|
||||
Each of these moves the whole capture \[em] the
|
||||
.IR .wav ,
|
||||
the JSON sidecar, the raw IQ where it was kept, the transcript and the decoded
|
||||
data \[em] because a recording in one directory and its transcript in another
|
||||
is a pair nothing will ever put back together. If the move cannot be finished,
|
||||
whatever has already moved is put back: half a capture in each of two places
|
||||
is worse than none moved at all.
|
||||
.PP
|
||||
The subdirectories are ordinary directories inside the recordings directory,
|
||||
so a scan writing there never looks into them and never lists what is in
|
||||
them. To read what is in one, point the browser at it:
|
||||
.IP
|
||||
.EX
|
||||
saunterbrowse ~/bandsaunter/{first_dir}
|
||||
.EE
|
||||
.PP
|
||||
.B u
|
||||
puts the last one filed back. One step, not a history: it exists so that a
|
||||
mistyped key costs nothing, not so that an evening's sorting can be unwound.
|
||||
.PP
|
||||
.B d
|
||||
deletes instead, and asks first, because nothing puts that back.
|
||||
.PP
|
||||
.B m
|
||||
is the other half of the same job. A birdie, a pager transmitter or a data
|
||||
link that fills the recordings directory night after night is not a recording
|
||||
problem, it is a scanning problem, and this writes the frequency into the
|
||||
lock-out list in your settings file. The width comes from the
|
||||
.B lockout_width
|
||||
setting, so a lock-out is a channel rather than a single point. Locking out a
|
||||
frequency does not delete what has already been recorded on it \[em] the two
|
||||
keys are separate on purpose, and pressing both is the usual thing to do.
|
||||
.SH DECODED DATA
|
||||
Where a capture carried data rather than speech, what was decoded takes the
|
||||
place of the transcript at the top of the screen: the kind of packet, and then
|
||||
|
|
@ -270,6 +340,15 @@ What was said, where a recogniser heard speech.
|
|||
.TP
|
||||
.IR ... _data.txt
|
||||
What was decoded, where the capture carried data.
|
||||
.TP
|
||||
.IR {filing_files}
|
||||
Where the filing keys move recordings to. Created on first use; a scan never
|
||||
looks in them.
|
||||
.TP
|
||||
.I ~/.config/bandsaunter/config.yaml
|
||||
The settings the
|
||||
.B m
|
||||
key writes a locked-out frequency into.
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B BANDSAUNTER_OUTPUT
|
||||
|
|
@ -328,11 +407,20 @@ to pick up recordings a running scan has written since.
|
|||
|
||||
|
||||
def main() -> int:
|
||||
dirs = [name for _, name, _ in FILING]
|
||||
text = PAGE.format(
|
||||
date=date.today().isoformat(),
|
||||
version=bandsaunter.__version__,
|
||||
sorts=", ".join(SORTS),
|
||||
players=", ".join(name for name, _ in PLAYERS))
|
||||
players=", ".join(name for name, _ in PLAYERS),
|
||||
filing_keys=" ".join(key for key, _, _ in FILING),
|
||||
filing_dirs=_english(f"{name}/" for name in dirs),
|
||||
filing_list=_english(f"{name}/" for name in dirs),
|
||||
filing_files=", ".join(f"{name} /" for name in dirs),
|
||||
first_dir=dirs[0],
|
||||
filing_prose="\n".join(
|
||||
f".TP\n.B {key}\nInto\n.IR {name} /\n\\[em] {why}."
|
||||
for key, name, why in FILING))
|
||||
text = text.replace("\n\n", "\n") # troff dislikes blank lines
|
||||
target = Path(sys.argv[1] if len(sys.argv) > 1
|
||||
else Path(__file__).parent / "saunterbrowse.1")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.\" Generated by packaging/make-browse-man.py -- do not edit by hand.
|
||||
.TH SAUNTERBROWSE 1 "2026-08-28" "bandsaunter 2026-08-28_03" "User Commands"
|
||||
.TH SAUNTERBROWSE 1 "2026-08-29" "bandsaunter 2026-08-29_01" "User Commands"
|
||||
.SH NAME
|
||||
saunterbrowse \- read and listen to what a bandsaunter scan collected
|
||||
.SH SYNOPSIS
|
||||
|
|
@ -34,8 +34,10 @@ With no
|
|||
it opens the one the scanner writes to, taken from your saved settings, so it
|
||||
normally needs no arguments at all.
|
||||
.PP
|
||||
It only ever reads. Nothing in the recordings directory is renamed, moved or
|
||||
deleted.
|
||||
Recordings can also be dealt with as they are read. A key files one into
|
||||
saved/, investigate/ and noise/, another deletes it outright, and another locks its frequency
|
||||
out of every later scan. Nothing else is written: without one of those keys
|
||||
the browser only reads.
|
||||
.SH KEYS
|
||||
.TP
|
||||
.B "Up Down k j"
|
||||
|
|
@ -77,6 +79,27 @@ it, and this picks up what has arrived since.
|
|||
Print the highlighted recording's path and quit, for piping into something
|
||||
else.
|
||||
.TP
|
||||
.B "S I N"
|
||||
File the highlighted recording into saved/, investigate/ and noise/ respectively \[em] the
|
||||
recording and every sidecar written beside it, together. See
|
||||
.B MANAGING THE RECORDINGS
|
||||
below.
|
||||
.TP
|
||||
.B u
|
||||
Put the last recording that was filed back where it came from. One level
|
||||
only, and a delete cannot be undone this way.
|
||||
.TP
|
||||
.B d
|
||||
Delete the highlighted recording and its sidecars for good. It asks first:
|
||||
this is the one key here that cannot be taken back.
|
||||
.TP
|
||||
.B m
|
||||
Lock the highlighted recording's frequency out, so that no later scan stops
|
||||
on it again. The frequency is written into your saved settings, the same list
|
||||
.BR bandsaunter (1)
|
||||
maintains, and takes effect on the next scan \[em] a scan already running read
|
||||
its settings when it started.
|
||||
.TP
|
||||
.B "? h"
|
||||
The list of keys, and which audio player was found.
|
||||
.TP
|
||||
|
|
@ -207,6 +230,59 @@ KML is the format Google Earth uses.
|
|||
.BR marble (1)
|
||||
and OsmAnd open it too, and it is XML, so a scan interrupted halfway through
|
||||
leaves a file that still opens.
|
||||
.SH MANAGING THE RECORDINGS
|
||||
A night's scan leaves hundreds of files, most of which are worth nothing and
|
||||
a few of which are the reason you left it running. Deciding which is which is
|
||||
what this program is for, and the keys that act on a recording are meant to
|
||||
be pressed once each, going down the list.
|
||||
.TP
|
||||
.B S
|
||||
Into
|
||||
.IR saved /
|
||||
\[em] keep this one.
|
||||
.TP
|
||||
.B I
|
||||
Into
|
||||
.IR investigate /
|
||||
\[em] come back to this one.
|
||||
.TP
|
||||
.B N
|
||||
Into
|
||||
.IR noise /
|
||||
\[em] not a signal worth keeping.
|
||||
.PP
|
||||
Each of these moves the whole capture \[em] the
|
||||
.IR .wav ,
|
||||
the JSON sidecar, the raw IQ where it was kept, the transcript and the decoded
|
||||
data \[em] because a recording in one directory and its transcript in another
|
||||
is a pair nothing will ever put back together. If the move cannot be finished,
|
||||
whatever has already moved is put back: half a capture in each of two places
|
||||
is worse than none moved at all.
|
||||
.PP
|
||||
The subdirectories are ordinary directories inside the recordings directory,
|
||||
so a scan writing there never looks into them and never lists what is in
|
||||
them. To read what is in one, point the browser at it:
|
||||
.IP
|
||||
.EX
|
||||
saunterbrowse ~/bandsaunter/saved
|
||||
.EE
|
||||
.PP
|
||||
.B u
|
||||
puts the last one filed back. One step, not a history: it exists so that a
|
||||
mistyped key costs nothing, not so that an evening's sorting can be unwound.
|
||||
.PP
|
||||
.B d
|
||||
deletes instead, and asks first, because nothing puts that back.
|
||||
.PP
|
||||
.B m
|
||||
is the other half of the same job. A birdie, a pager transmitter or a data
|
||||
link that fills the recordings directory night after night is not a recording
|
||||
problem, it is a scanning problem, and this writes the frequency into the
|
||||
lock-out list in your settings file. The width comes from the
|
||||
.B lockout_width
|
||||
setting, so a lock-out is a channel rather than a single point. Locking out a
|
||||
frequency does not delete what has already been recorded on it \[em] the two
|
||||
keys are separate on purpose, and pressing both is the usual thing to do.
|
||||
.SH DECODED DATA
|
||||
Where a capture carried data rather than speech, what was decoded takes the
|
||||
place of the transcript at the top of the screen: the kind of packet, and then
|
||||
|
|
@ -252,6 +328,15 @@ What was said, where a recogniser heard speech.
|
|||
.TP
|
||||
.IR ... _data.txt
|
||||
What was decoded, where the capture carried data.
|
||||
.TP
|
||||
.IR saved /, investigate /, noise /
|
||||
Where the filing keys move recordings to. Created on first use; a scan never
|
||||
looks in them.
|
||||
.TP
|
||||
.I ~/.config/bandsaunter/config.yaml
|
||||
The settings the
|
||||
.B m
|
||||
key writes a locked-out frequency into.
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B BANDSAUNTER_OUTPUT
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue