Back it up, and move it#
Two things are irreplaceable, and neither is the software. somnia.db holds
where you are in every book, how far you have heard, and the index that makes
semantic seek possible. The library holds hours of rendered audio that cost
hours of CPU. Everything else — the virtual environment, the settings file, the
catalog, and the joined-up copies under streams — can be rebuilt in ten
minutes.
Back up the database while it is running#
somnia runs in WAL mode, so copying the file with cp while the server is up
can give you a torn database. sqlite’s own backup does it safely, and the
virtual environment already has everything needed:
~/somnia-venv/bin/python - <<'PY'
import sqlite3
src = sqlite3.connect("/home/you/.local/share/somnia/somnia.db")
dst = sqlite3.connect("/backup/somnia.db")
with dst:
src.backup(dst)
PY
The library is ordinary files that are only ever appended to, so it needs nothing clever:
rsync -a ~/library/audiobooks/ /backup/audiobooks/
A render in progress means a partial chapter in the copy, which the next render overwrites. It is not a reason to stop the world.
Move both to another machine#
Install somnia on the new box first — Installation
— then bring the data across, and set SOMNIA_LIBRARY_DIR to wherever the
library has landed.
Then there is one thing to fix, and it will not announce itself.
The paths inside the database#
chapters.audio_file holds absolute paths, written when the chapter was
rendered. Move the library to a different path — a different user, a different
mount, /srv instead of /home — and every row points somewhere that does not
exist. The server does not fall back to guessing: chapters are looked up in the
database and never by path, and a row resolving outside SOMNIA_LIBRARY_DIR is
refused even if the file is there, because a database carried from another
machine can point anywhere.
What you would see is a page that lists your books, answers questions, and plays nothing.
Stop the server and rewrite the prefix:
~/somnia-venv/bin/python - <<'PY'
import sqlite3
db = sqlite3.connect("/home/you/.local/share/somnia/somnia.db")
with db:
n = db.execute(
"UPDATE chapters SET audio_file = replace(audio_file, ?, ?)",
("/home/old/library/audiobooks", "/srv/audiobooks"),
).rowcount
print(f"{n} chapters repointed")
PY
Then let somnia-doctor.sh (in the repo’s scripts/, or curl it as
Installation does) confirm it, which is exactly
what it is for:
bash somnia-doctor.sh
It checks every chapter row for a file that exists and sits inside
SOMNIA_LIBRARY_DIR, and names the first one that does not.
What you do not need to move#
The catalog is a download: somnia catalog-update on the new box rebuilds it.
The virtual environment should be built fresh rather than copied, since it holds
absolute paths of its own.
SOMNIA_DATA_DIR/streams is the other one, and it is the one that will tempt
you, because it sits beside somnia.db and it is by far the largest thing in
there — normally about the size of the library again, since it holds each book’s
chapters joined into the one file the page plays
(ADR 7).
Normally, not always. A join is written as streams/<gid>/<n>.m4a, where n is
how many chapters it covers, so a book opened while it was still being rendered
can leave earlier and shorter joins behind it — and nothing deletes them, which
ADR 7 argues for deliberately rather than having overlooked. A book rendered
before anybody listened to it has exactly one. So size this by looking rather
than by multiplying: du -sh the directory says what is actually there.
It is a cache either way. Every file in it is rebuilt from the library in a
second or two of ffmpeg -c copy the first time somebody opens that book, so
back it up and you are paying to store a copy of audio you already backed up,
and leave it behind and you lose nothing at all — including the stale joins,
which is the tidiest moment there is to be rid of them. If you rsync the data
directory rather than copying somnia.db on its own, exclude it.
Moving the renders somewhere faster#
This is not hypothetical — somnia’s own renders moved to a home machine, which is where the 3.87× in ADR 7 comes from, and the absolute-path catch below is the one thing that bit.
Rendering and serving do not have to be the same machine, and the render host is the one that wants CPU (#4). The catch is the same one as above: whichever box renders writes absolute paths into the database, so either both machines see the library at the same path — a shared mount, or the same directory name on both — or you rewrite the prefix each time you bring a book across. The same path on both is much the easier of the two.