Package the speech recogniser and its model for apt

No speech recogniser is in Debian, so installing bandsaunter from a .deb
left transcription to a manual pip step on every machine. A repository of
one's own is not bound by archive policy, so build-repo.sh now packages
faster-whisper and the base.en model alongside the application:

  bandsaunter                the application (Architecture: all)
  bandsaunter-transcribe     faster-whisper, vendored (amd64)
  bandsaunter-model-base-en  the model, so nothing reaches the network

The wheels land in /usr/lib/bandsaunter/vendor rather than dist-packages,
and transcribe.py appends that directory to sys.path -- appends, so an
apt-managed numpy or PyYAML still wins and the vendor copy only fills the
gap. Duplicates of what Debian already ships are stripped from the tree.
resolve_model() turns a bare "base.en" into the packaged copy when one is
installed, and leaves it alone to be downloaded when none is.

The app package recommends the other two, so "apt install bandsaunter"
brings the lot and --no-install-recommends still gets just the scanner.
Its postinst explains how to add a recogniser only when there genuinely
is not one -- including the case where apt has already unpacked the
recogniser package but not yet configured it.

Verified with the source tree hidden and no home directory: the packaged
CLI runs, and a real recording transcribes offline from the vendored
engine and packaged model while numpy still resolves to the system one.
apt itself resolves the repository over HTTP and plans all three.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
The Dust Council 2026-08-21 22:12:24 -07:00
parent 16f3128690
commit 8d94a52942
5 changed files with 304 additions and 22 deletions

View file

@ -38,6 +38,52 @@ sudo apt install ./dist/bandsaunter_*.deb
apt pulls in every dependency itself, and the package blacklists the DVB-T
driver that would otherwise claim the receiver. Nothing else to do.
Speech transcription is the one part that cannot come from Debian, because no
speech recogniser is packaged there. Installed this way, everything else works
and the install prints a short note saying how to add one. To have that arrive
by apt as well, build the repository below instead.
### From your own apt repository
For installing on several machines, or on a fresh one, without hunting for the
recogniser afterwards. `build-repo.sh` builds three packages and an apt index:
| Package | Arch | Contents |
|---|---|---|
| `bandsaunter` | all | the application |
| `bandsaunter-transcribe` | amd64 | faster-whisper and its dependencies, in `/usr/lib/bandsaunter/vendor` |
| `bandsaunter-model-base-en` | all | the `base.en` model, in `/usr/share/bandsaunter/models` |
```bash
./packaging/build-repo.sh # writes dist/repo/
rsync -a dist/repo/ server:/var/www/html/bandsaunter/
```
Serve that directory over HTTP from anywhere on the LAN, then on each machine:
```bash
echo 'deb [trusted=yes] http://server/bandsaunter ./' \
| sudo tee /etc/apt/sources.list.d/bandsaunter.list
sudo apt update
sudo apt install bandsaunter
```
That single command brings the recogniser and its model too — they are
`Recommends`, which apt installs by default. `--no-install-recommends` gets
just the application. Nothing reaches the network afterwards: the model is on
disk, so the first transcription works offline.
`[trusted=yes]` skips signing, which is the sensible trade on a private LAN.
To sign it instead, run `gpg --clearsign` over `dist/repo/Release` to produce
`InRelease` and drop the `[trusted=yes]`.
The vendored packages are appended to `sys.path`, never prepended, so anything
apt provides — numpy, PyYAML — still wins; the vendor directory only fills the
gap Debian leaves. `BANDSAUNTER_VENDOR_DIR` and `BANDSAUNTER_MODEL_DIR`
override both locations.
Rebuilding for a new version is the same command; `apt upgrade` picks it up.
### From source
```bash
@ -79,13 +125,18 @@ pip install vosk # ~10 MB plus a 40 MB model, weaker on noise
bandsaunter transcribe --engines
```
That is why transcription is `Suggests:` rather than `Depends:` in the
package. Debian Policy forbids anything in the archive from requiring
software outside it, and a `postinst` that reaches out to PyPI would break
offline and reproducible installs — so a package simply cannot pull these in.
Transcription is therefore off by default and reports plainly when no
recogniser is present, rather than the install failing or the feature
appearing broken.
That is why the plain `.deb` cannot depend on one. Debian Policy forbids
anything in the archive from requiring software outside it, and a `postinst`
that reaches out to PyPI would break offline and reproducible installs — so a
package in the archive simply cannot pull these in. Transcription is therefore
off by default and reports plainly when no recogniser is present, rather than
the install failing or the feature appearing broken.
A repository of your own is not bound by that rule, which is what
[`build-repo.sh`](#from-your-own-apt-repository) exploits: it packages
faster-whisper and its model itself, into a private directory rather than into
`dist-packages`, and lets apt install them alongside. Nothing is downloaded at
install time, and nothing collides with an apt-managed module.
Mixing the two is nonetheless fine here. Modern Debian marks the system
Python as externally managed (PEP 668), so a pip install lands in your user
@ -100,11 +151,11 @@ from the `.deb` into `/usr/lib/python3/dist-packages` picks it up with no
further configuration — verified, not assumed. A virtual environment works
too, as long as bandsaunter runs inside it.
If you would rather keep everything under apt, the only route is packaging a
recogniser for Debian yourself. That is a real undertaking for whisper: it
would mean packaging ctranslate2, tokenizers, onnxruntime and their
dependencies, several of which are large C++ or Rust projects. It is why
none of them are there.
Getting these into Debian proper would be a different matter: it would mean
packaging ctranslate2, tokenizers, onnxruntime and their dependencies, several
of which are large C++ or Rust projects, each to archive standards. That is
why none of them are there, and why the local repository vendors the wheels
instead of trying to do it properly.
### Other distributions
@ -682,7 +733,8 @@ depends on a trained model, so there is no built-in fallback.
```bash
bandsaunter transcribe --engines # what is installed
pip install faster-whisper # the recommended one
sudo apt install bandsaunter-transcribe # from your own repository
pip install faster-whisper # or straight from PyPI
```
| Engine | Notes |