The server could transcode and serve HLS; nothing could play it. Chrome has no native HLS support, so without this the feature existed only for curl. hls.js is the only real option for HLS in Chrome, and it is MIT. It is loaded with a dynamic import, so the ~185 KB gzip is paid only when a transcode actually happens - a direct play, which is the common case, fetches none of it. Safari and iOS play HLS natively, so those get the playlist URL directly and never load the library at all. Three failure paths all fall back to the direct file URL rather than leaving a dead player: hls.js not supported, the dynamic import failing, and a fatal playback error. Transcoding is an optimisation and must never be able to break playback - a file that would have direct-played has to keep working when the encoder or the library is unavailable. The instance is destroyed on source change and on unmount, so seeking between files does not leave encoders or MediaSource objects behind. Verified: pnpm check 0/0, the production build succeeds, and hls.js is emitted as its own lazy chunk rather than folded into the main bundle. Not verified in a browser - there is no automated browser test in this repo, so the actual playback of a transcoded stream is unproven end to end. The server side of that path is verified separately against real hardware. |
||
|---|---|---|
| .. | ||
| dist | ||
| public | ||
| src | ||
| .gitignore | ||
| embed.go | ||
| index.html | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.ts | ||
Vellum web
Svelte 5 + Vite + TypeScript single-page app. It is built to a static bundle in
dist/ and embedded into the Go binary, so all asset paths are relative
(base: './' in vite.config.ts).
Commands
pnpm install
pnpm dev # dev server on :5173, proxying /api to :7788
pnpm build # writes dist/
pnpm check # svelte-check
Run the Go server on :7788 (make run from the repo root) and pnpm dev
proxies API and health requests to it, so cookies and sessions behave exactly
as they will in production.
Layout
src/
main.ts entry point
App.svelte login gate -> shell
app.css design tokens and base styles
lib/
api.ts typed client; mirrors the Go handlers
session.ts auth + library stores
nav.ts view state
components/
Login.svelte
Shell.svelte nav + view switch
Libraries.svelte library list, create, scan, delete
LibraryView.svelte item grid with covers
Reader.svelte epub.js reader with progress sync
Users.svelte admin user management
The reader resumes from a spine index, which is why progress positions are
integers: it round-trips through the polymorphic progress table without
needing a text column.