de eerste week van movieroulette.nl/app
Een biografie in release notes, sleutelbossen vol JSON en één hardnekkige base64-blob
Dag 1: het lege scherm
Elk verhaal over software begint hetzelfde: een leeg bestand en een idee. In dit geval een bijzonder eigenwijs idee — geen sleek streaming-dashboard met witruimte en pastelkleurtjes, maar een filmkeuzemachine die eruitziet alsof hij in 1987 uit een videotheek is gerold. Knoppen die klikken als een echte VCR. Een reel die ratelt langs titels tot hij vastklikt op één film. Ladingen aan gouden letters op fluweelzwart.
Het uitgangspunt was koppig simpel: één bestand. Geen build-proces, geen frameworks, geen node_modules-oerwoud — gewoon index.html, met alles erin. HTML, CSS, JavaScript en uiteindelijk duizenden films, allemaal in één document dat je met een simpele upload naar WordPress kon slingeren.
Het klinkt naïef. Het bleek verrassend ver te komen.
De databron: Plex, mixed content en een vermomde hostname
Een filmroulette zonder films is een lege reel die eeuwig blijft draaien, dus de eerste serieuze horde was de koppeling met de Plex-bibliotheek. Dat klonk eenvoudiger dan het was: de browser weigerde consequent te praten met de lokale Plex-server op 127.0.0.1:32400, simpelweg omdat een HTTPS-pagina geen boodschap wil hebben aan onbeveiligd lokaal verkeer — mixed content, zei de browser, en sloeg de deur dicht.
De oplossing dook op in een onverwachte hoek: Plex bleek zelf al een eigen HTTPS-relay-hostname te gebruiken (die plex.direct-truc), verstopt in de performance-logs van de pagina. Eenmaal gevonden, ging de deur alsnog open — met een X-Plex-Token uit localStorage en een simpele paginate door /library/sections/1/all.
Het resultaat: duizenden titels, keurig geëxporteerd, omgezet naar een array, en torrent-achtige bestandsnamen automatisch schoongepoetst tot leesbare titels.
De array die te groot werd voor zijn eigen bestwil
Wat volgde was een aanhoudende technische running gag: regel 39. Ergens in het bestand nestelde zich een base64-blob van ruim 1.1MB — een enkele, absurd lange regel die elke view, sed of awk-opdracht met een regelbereik om zeep hielp zodra die regel er ook maar per ongeluk in werd meegenomen. De les werd al snel hard geleerd: nooit een bereik gebruiken dat regel 39 raakt, en voor alles wat met de filmdata te maken had, was python3 met re.search en json.loads de enige betrouwbare weg naar binnen.
Diezelfde discipline gold voor de MOVIES-array zelf, die op één enkele, kilometerslange regel staat. grep gaf ‘m netjes terug — alleen was “netjes” hier een woord van niks, want de output was volstrekt onleesbaar. Vanaf dat moment werd elke wijziging aan de filmdata via gestructureerde parsing gedaan, nooit via blinde tekstmanipulatie.
Orde in de chaos: genres, talen en een tolk voor elk seizoen
Een Plex-bibliotheek die jarenlang organisch is gegroeid, brengt rommelige metadata met zich mee. Genres stonden soms in het Nederlands, soms in het Engels, soms allebei door elkaar — “Misdaad” naast “Crime”, “Sciencefiction” naast “Sci-Fi”. Daar kwam de GENRE_MAP bij: één canonieke sleutel per genre, ongeacht hoe de brondata het spelde.
En omdat de app zeven talen moest spreken — Nederlands, Engels, Frans, Duits, Italiaans, Spaans en Portugees — werd elke canonieke genresleutel vervolgens vertaald naar alle zeven. Vlaggetjes rechtsboven, een taalmenu dat live omschakelt, en zelfs de kleinste labeltjes in het infopaneel volgen trouw mee zodra iemand van taal wisselt.
Het infopaneel: een klein venster met grote ambities
Ergens halverwege de week kreeg de reel gezelschap van een INFO-knop — veertig bij veertig pixels, net als alle andere knoppen op het VCR-dashboard, met een eigen groen lampje dat aangeeft of het paneel open staat. Erachter school een paneeltje van amper 132 pixels hoog, waarin toch een filmposter, titel, jaartal, speelduur, genres en de eerste twee acteurs moesten passen — zonder dat het paneel pas open mocht gaan als er ook daadwerkelijk iets zinnigs te tonen viel.
Klein scherm, grote logistiek: het paneel activeert alleen als duur, genres én cast compleet zijn; de poster is decoratief en optioneel.
De grote posterjacht
En toen kwam het probleem dat weken zou blijven terugkomen: bijna geen enkele film had een poster. Infrastructuur stond klaar — het element, de CSS, de show/hide-logica — maar de data zelf was overal null.
De oplossing werd een combinatie van geduld en een strak protocol: TMDB’s API, aangeroepen met een titel-en-jaar-matchfunctie die genormaliseerde titels vergelijkt, exacte jaartal-matches beloont, en twijfelgevallen apart op een stapeltje legt om later handmatig te beoordelen. In batches van vijftien tot twintig titels tegelijk (groter liep vast op afgekapte tool-output), stap voor stap, sessie na sessie.
Het resultaat na een aantal sessies: van de 6.655 films hadden er 6.584 een poster. Een lijst van rond de veertig titels bleef hardnekkig onvindbaar — vaak omdat de titel in de eigen data net anders gespeld was dan op TMDB, of omdat het simpelweg te obscuur was om een betrouwbare match te vinden. Die lijst ligt nu vastgelegd zodat toekomstige sessies er niet steeds opnieuw tegenaan lopen.
Een bijzonder detail: zeven films bleken helemaal geen metadata-object te hebben, alleen een kale titel, jaar en slug. Vijf daarvan zijn inmiddels volledig aangevuld — inclusief een Stephen King-miniserie die is samengevat tot 254 minuten, en een Metropolis-poster die zijn cast en genres “leende” van de originele 1927-versie omdat de eigen TMDB-entry zelf leeg was. Twee blijven nog open, wachtend op een betere match.
De PWA die niet wilde stilzitten
Geïnstalleerd als PWA op Android bleek de app een eigenzinnige gewoonte te hebben: bounce-scrollen zodra je ‘m herlaadde, met een adresbalk die in- en uitklapte en het paneel liet meeschuiven. De oplossing zat ‘m in een stapeling van kleine CSS-trucjes: position:fixed met inset:0 op de body, 100dvh met een 100vh-fallback, overflow:hidden en overscroll-behavior:none om de bounce definitief de kop in te drukken. Op mobiel bleek bovendien align-items:flex-start nodig in plaats van center, met een min(193px, 20vh)-marge zodat het paneel niet buiten beeld schoot op korte viewports.
Het hartje verschijnt — twee keer
Precies een week na de eerste lege index.html kreeg de app iets dat níet over films ging, maar over de app zelf: een like-knop. Een leeg hartje linksboven, dat rood kleurt en blijft zodra iemand erop klikt — en een getal ernaast dat voor iedereren, op elk apparaat, hetzelfde is.
Dat laatste vroeg om iets nieuws: een klein PHP-bestand naast index.html, dat een teller bijhoudt in een JSON-bestand op de server, met file-locking zodat twee gelijktijdige klikken elkaar niet in de weg zitten. Geen database, geen framework — gewoon een bestandje dat netjes telt.
Niet lang daarna volgde de logische vervolgvraag: kan dat ook per film? Het antwoord was ja, met één addertje onder het gras — de meest voor de hand liggende sleutel per film bleek bij een flink deel van de catalogus leeg te zijn. In plaats daarvan werd titel+jaar de sleutel: altijd aanwezig, altijd uniek genoeg. Hetzelfde PHP-bestand kreeg er een movies-object bij, en rechtsboven in het infopaneel verscheen een kleiner broertje van het hartje — netjes uitgelijnd op de pixel met de rand van de poster ernaast.
De stand na één week
Wat begon als een leeg HTML-bestand is nu:
- 6.646 films, met genres genormaliseerd over Nederlands én Engels door elkaar
- Zeven talen, live om te schakelen, tot in elk labeltje van het infopaneel
- 6.584 posters, film voor film opgespoord via TMDB
- Twee soorten like-tellers, wereldwijd gedeeld via een simpel PHP-bestandje
- Eén PWA die niet meer bounce-scrollt op Android
- Nul frameworks, nul build-stappen, en nog altijd precies één
index.html
Niet slecht voor een week waarin het grootste obstakel soms letterlijk gewoon “regel 39” heette.




0 reacties