Disclosure: I built RKMovies, a small 4K media player client for Android and Windows. This post is a build diary of its web shell.
Most PWA advice ends at "cache everything". That breaks the moment you ship an update, because users keep running old JS against new HTML. For a media player that is not cosmetic. It is broken playback.
So the shell of this offline-first PWA goes network-first, with one hard rule: code assets are only ever served from an exact URL match.
The install handler precaches the shell with versioned URLs:
const CACHE = "rkmovies-shell-v43";
const SHELL = ["/", "/index.html", "/style.css?v=135", "/adgate.js?v=74",
"/updater.js?v=71", "/app.js?v=157", "/vendor/artplayer.js?v=59",
"/manifest.webmanifest"];
The fetch handler passes every /api/ and /proxy request straight to the network (never cache those), and for same-origin GETs it tries the network first. Cache lookup for code assets (app.js, parts/*, vendor/*) uses exact match only, so a new ?v= can never be answered by an old cached build. Non-code assets fall back to ignoreSearch matching, then to the cached index.html shell. Anything else gets a plain 503 offline. No stale-code fallback, ever.
The update side lives in updater.js: a remote config fetched from a public GitHub repo with a 3 second timeout, throttled to once per 24 hours, with the last good config kept in localStorage as fallback. If the fetch fails, the app just runs on the cached config.
The point of all this: the media player without ads is 0.91 MB, the site installs as a PWA in about a second, and once installed it works offline except for the streams themselves. There is also a Windows build for old PCs.
If you want to poke at it, the site is rkmovies.dpdns.org.
Top comments (0)