A dead-simple, installable web app (PWA) that plays a family's own Bible-story MP3s. One job: a kid taps a title, the story plays. No accounts, no ads, no backend. The interface is entirely in Greek.
Source: https://github.com/GiorgosPap777/Bible-Stories
The recordings are not in the image — they are yours, and they are hundreds of megabytes. Mount them:
docker run -d --name stories -p 8420:8080 \
-v /srv/stories/audio:/usr/share/nginx/html/audio:ro \
-v /srv/stories/stories.json:/usr/share/nginx/html/data/stories.json:ro \
giorgospap777/bible-stories
Open http://localhost:8420. Without those mounts the app loads and every story returns 404.
With compose:
services:
stories:
image: giorgospap777/bible-stories:latest
restart: unless-stopped
ports:
- "8420:8080"
volumes:
- ./audio:/usr/share/nginx/html/audio:ro
- ./stories.json:/usr/share/nginx/html/data/stories.json:ro
nginx:alpine-slim plus the static app — about 22 MB. No build step, nothing to
keep patched but nginx. It listens on 8080 and serves an ordinary static
site, so a reverse proxy in front only has to terminate TLS.
The image handles the things a static host has to get right for this app:
no-store on service-worker.js, so a stale worker never gets pinned.opus, oga, m4a, aac,
flac, wav. An unknown type is served as application/octet-stream, which
the nosniff header then forbids the browser from decoding, and the story
simply refuses to play.The list is data/stories.json, mounted from outside. Order in the file is play
order:
{
"id": "ionas",
"title": "Ιωνάς - μια σπουδαία αποστολή",
"file": "audio/our-stories/ionas.opus"
}
id is the key the saved playback position is stored under, so keep it stable.
localStorage.Streaming playback goes straight to the network — the service worker
deliberately stays out of the media path, because proxying a media stream
through a worker makes Chrome raise MEDIA_ERR_NETWORK on every seek that
cancels an in-flight range request.
Storing a story for offline is therefore explicit: Ρυθμίσεις → Αποθήκευση αυτής της ιστορίας downloads it with progress and keeps going in the background. The store is capped (500 MB by default), evicts oldest-first, and the panel shows what is used and can clear it.
latest, 1.1.2 — current. Offline downloads run one at a time, so the
500 MB cap holds even when two tabs ask at once, and a new deploy is picked
up on the home screen instead of interrupting a story.1.1.1 — the real app icon.1.1.0 — explicit, bounded offline storage; the worker stays out of playback.1.0.0 — first release; cached audio automatically, which broke seeking on
slow connections. Superseded.Content type
Image
Digest
sha256:d57231fea…
Size
6 MB
Last updated
3 days ago
docker pull giorgospap777/bible-stories