Way back in 2021 I began the adventure of building my own media server for audiobooks and podcasts with a
Iāve done a bad job tracking development but Iāll attempt a brief history:
Dev Timeline
v1 was bootstrapped with an embarrassingly hacky SQLite interface. My solution stress tested the phrase āit aināt stupid if it worksā. If you can figure out what I was doing, just know that I was fully aware of how stupid it was! Itās remarkable how ātemporaryā turns into
v2 was a major rebuild with SvelteKit. I made a custom API that dropped Subsonic compatibility. I used a proper SQLite module. I later changed the podcast syncing to use a 3rd-party index. That proved unreliable so I did the unthinkable and parsed XML myself.
v3 was another major refactor (Iām losing track). I dropped SvelteKit in favour of my own scuffed library and HTMX inspired web components. On a fateful whim I replaced SQLite with Deno KV. I refreshed the design too.
Along this journey Iāve learnt: natural sorting, relative formatting, media session, file storage, off-screen canvas, and so much more. Including the fact that not a single person on earth is capable of publishing a valid and logical RSS feed (I blogged one of many issues).
RIP Deno KV
In April I wrote about Denoās Decline. My post topped āthe usual placesā and elicited an official response. I wasnāt acknowledged directly, but no one else was blogging about Deno. Iād have enjoyed a shoutout but either way it doesnāt look good. Dahlās update confirmed that Deno KV is dead without saying it out loud (VCs spook easily).
SQLite Lives
This left me two options:
- Remain on Deno KV
- Return to SQLite
Option one was a valid choice. KV still works and Iām fully self-hosted. Option two would allow me to attempt SQLite again (third timeās a charm). Iāve since gained more tools from building several SvelteKit apps. This time Iād use Arktype and Drizzle instead of stubbornly hand-rolling SQL.
Due to the aforementioned RSS feed woes, my āperfectā KV architecture was suffering a few setbacks. I wrongly assumed 3rd-party data would be in any shape coherent. How naive I was. I canāt blame Deno for that! New KV APIs might have presented better solutions, but with KV shelved I chose option two.
One architectural success I made was wrapping the database in a CRUD-like API. This provided functions like getPodcast
and deleteEpisode
ā simple stuff.
I first rewrote podcast related functions backed by SQLite. Then work got busy for weeks, leaving me with an app running off two databases. I awoke at 5am today to finish the job for audiobooks. The most rewarding part of this update was the migration script:
import * as kv from "./src/kv/mod.ts";
import * as db from "./src/sqlite/mod.ts";
for (const podcast of await kv.getPodcasts()) {
await db.setPodcast(podcast);
for (const episode of await kv.getEpisodes(podcast.id)) {
await db.setEpisode(episode);
}
}
The script iterated over entities from the old KV database and inserts then into the new SQLite database. The power of abstraction!
In hindsight Deno KV was a bad bet. You live and learn. This is why I experiment on personal projects and not client work. sauroPod is on GitHub if you fancy a look. Despite the Docker file it wonāt run on your machine without undocumented extras. Maybe one day when Iām done refactoring Iāll release a usable version.
So long Deno KV š«” you served me well(-ish).