Halcyon Jellyfin videotheek installeren: handleiding
Transformeer uw Jellyfin-bibliotheek naar een virtuele videotheek uit de jaren 90 met Halcyon. Leer hoe u de Docker-container configureert en de reverse proxy instelt.
Wat Halcyon doet met uw Jellyfin-bibliotheek
Halcyon Video tekent uw Jellyfin-bibliotheek opnieuw als een beloopbare videotheek uit de jaren 90 in de browser. Elke film die u bezit, wordt een hoesje op een schap. U loopt door de gangpaden onder de tl-verlichting, pakt een doosje van de plank, draait het om om de specificaties op de achterkant te lezen en brengt het naar de balie om het afspelen te starten. Het afspelen rapporteert de start, voortgang en stop terug naar Jellyfin, zodat hervattingspunten en de kijkgeschiedenis correct blijven.
Halcyon leest een bestaande Jellyfin-server via de Jellyfin API en houdt geen eigen bibliotheek bij. Deze handleiding gaat ervan uit dat Jellyfin al draait en correct scant. Als dat niet het geval is, stel dan eerst Jellyfin in als mediaserver op een VPS en kom terug zodra uw bibliotheek er goed uitziet in de normale webclient. Dit is het type software dat u installeert omdat de bibliotheek er al is, niet omdat u een extra service nodig had op uw lijst met zelfgehoste applicaties.
Het project is GPL-3.0 en geschreven door één persoon, en de README stelt duidelijk dat er geen pull requests worden geaccepteerd. De ontwikkeling gaat snel en er is geen tweede beheerder om een regressie op te vangen, dus zet de image-versie vast voordat u de winkel aan iemand anders laat zien. Het laatste gedeelte beschrijft hoe u dit doet.
Waar vindt de rendering plaats?
In de browser. Halcyon is een Vite- en TypeScript-applicatie gebouwd op three.js, een JavaScript-bibliotheek die 3D-graphics tekent via WebGL (web graphics library, de interface van de browser naar de GPU). De winkelgeometrie en de box art worden samengesteld door de machine die het scherm aanstuurt.
De container doet zeer weinig. Deze voert npm run serve uit, wat vite preview --port 1420 --strictPort --host is, en serveert de gebouwde bestanden plus enkele kleine middleware-routes. Halcyon voegt geen transcoding toe en draait geen engine op de server.
De GPU-vraag heeft dus betrekking op de client. Een kleine VPS serveert dit zonder problemen, omdat het serveren neerkomt op statische bestanden via HTTP. De laptop, tablet of televisie die de browser draait, bepaalt of de winkel soepel beweegt of traag reageert.
Eén functie doorbreekt die regel. Remote Play start headless Chromium-instanties op de server en streamt de gerenderde winkel naar een telefoon of settopbox via WebRTC (web real time communication). Dat pad rendert op de server, standaard beperkt tot twee instanties en aanpasbaar met REMOTE_PLAY_MAX_INSTANCES. Zonder een gemapt /dev/dri-apparaat renderen die instanties op de CPU, waardoor een VPS met twee cores elke extra kijker merkt.
Wat de winkel leest uit uw bibliotheek
De gangpaden zijn afkomstig uit de eigen structuur van Jellyfin. Halcyon deelt secties in op basis van uw bibliotheken en genres, en groepeert vervolgdelen uit uw BoxSets. De specificaties die op de achterkant van elk doosje staan, zijn afkomstig uit de MediaStreams-metadata die Jellyfin al beheert. Dit betekent dat alles wat in Jellyfin ontbreekt, ook op de plank ontbreekt.
Hierdoor is de winkel een getrouwe weergave van uw metadata. Een bibliotheek die wordt gevoed door een arr-stack in Docker Compose, inclusief artwork en ingevulde genres, ziet er hier veel beter uit dan een map met losse bestanden en generieke namen. Fotobibliotheken zijn op dezelfde manier afhankelijk van de software die ze heeft geïndexeerd. Dit is belangrijk om te onthouden wanneer u PhotoPrism tegenover Immich afweegt voor de foto's die op dezelfde server staan.
Probeer de videowinkel-demo voordat u iets installeert
Het project publiceert de volledige winkel die draait op een synthetische bibliotheek via de gehoste demo. Het toevoegen van ?demo=1 aan een willekeurige Halcyon-URL doet hetzelfde op uw eigen implementatie.
Gebruik dit als een hardwaretest, met één kanttekening: de demo past de grootte aan op basis van het apparaat. Een browser met voldoende GPU-capaciteit krijgt de volledige winkel van ongeveer 2.000 titels te zien. Een gemiddelde GPU krijgt er ongeveer 800. Een telefoon, of elke browser die 4 GB of minder aan apparaatgeheugen rapporteert, krijgt er ongeveer 300. Een demo die soepel draait op een zwak apparaat heeft zichzelf al verkleind; dit zegt dus weinig over hoe uw volledige bibliotheek daar zal presteren. De nuttige test is een apparaat dat de volledige winkel laadt. Als dat stottert, zal uw eigen bibliotheek van vergelijkbare omvang ook stotteren. De oplossing is dan de 2.5D-modus die hieronder wordt beschreven, in plaats van een krachtigere VPS.
Uitvoeren met Docker
Dit is het commando dat de upstream-documentatie voorschrijft.
docker run -d --name halcyon --network host --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoControleer vervolgens of de service is opgestart.
docker logs halcyon
curl -I http://127.0.0.1:1420De log moet tonen dat de preview-server luistert op poort 1420 en curl moet antwoorden op HTTP/1.1 200 OK. Een container die binnen enkele seconden afsluit, heeft bijna altijd een poortconflict. --strictPort betekent dat de server weigert uit te wijken naar 1421 wanneer 1420 bezet is, waardoor deze stopt.
--network host is bedoeld voor Remote Play, niet voor de store. WebRTC moet het werkelijke adres van de machine adverteren aan het apparaat dat de stream wil ontvangen. Achter de standaard Docker-bridge kent de container alleen zijn eigen 172.x-adres, dat geen enkele telefoon op uw netwerk kan bereiken, waardoor de stream nooit verbinding maakt. Als u de store alleen in een browser wilt gebruiken, publiceer dan de poort.
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoDat is de betere standaardinstelling op een VPS, omdat host-networking de container op elke interface van de machine plaatst, inclusief de publieke interface. Docker draaien op een VPS behandelt de rest van die afweging. --restart unless-stopped zorgt ervoor dat de store na een reboot weer terugkeert; dit werkt volgens hetzelfde principe als Compose-services die opstarten bij boot.
Het klonen van de repository en het uitvoeren van docker compose up -d bouwt de image lokaal. Het meegeleverde Compose-bestand bouwt standaard vanuit de broncode en bevat de vooraf gebouwde image:-regel als commentaar; verwijder het commentaarteken voor deze regel als u de gepubliceerde image via Compose wilt gebruiken.
De gepubliceerde image ondersteunt zowel linux/amd64 als linux/arm64. Gecontroleerd op 9 oktober 2026: de huidige 0.25.0-release vermeldt een afzonderlijke digest voor elk platform, dus een arm64 VPS haalt de image op met hetzelfde commando als elke andere host. De periode waarin alleen amd64 beschikbaar was, was kort: de changelog verwijderde de arm64-image in v0.2.1 op 8 augustus 2026 en voegde deze weer toe, gebouwd op native runners, in v0.4.0 op 11 augustus 2026. Alleen een host die vastgepind is op een tag uit dat tijdsvenster moet de image vanuit de clone bouwen.
Verwijs naar uw Jellyfin-server
Open http://<host>:1420 en log in met het adres, de gebruikersnaam en het wachtwoord van uw Jellyfin-server. Het bestand .env.local.example in de repository is uitsluitend bedoeld voor lokale ontwikkeling. Vite stelt variabelen met het voorvoegsel VITE_ beschikbaar aan client-side code; een Jellyfin-wachtwoord dat hierin wordt geplaatst, wordt gecompileerd in de JavaScript-bundel die elke bezoeker downloadt. Log op een server die toegankelijk is voor anderen in via de interface.
De browser communiceert rechtstreeks met Jellyfin. De container van Halcyon fungeert niet als proxy voor de Jellyfin API, en dit heeft twee gevolgen waar u rekening mee moet houden voordat u begint met debuggen.
Ten eerste moet Jellyfin bereikbaar zijn vanuit de browser, niet alleen vanaf de VPS die Halcyon host. Een Jellyfin-instantie die is gebonden aan 127.0.0.1:8096 is prima voor een lokale test, maar zorgt ervoor dat de schappen voor anderen leeg blijven.
Ten tweede betreft het een cross-origin verzoek, van het adres van Halcyon naar dat van Jellyfin. Jellyfin beantwoordt API-verzoeken standaard met Access-Control-Allow-Origin: *, waardoor het zonder extra configuratie werkt. Als u deze instelling heeft beperkt, of een authenticatie-proxy voor de Jellyfin API heeft geplaatst, rapporteert de browserconsole blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource en laadt de winkel met lege schappen.
Plaats de service achter een reverse proxy, met authenticatie aan de voorzijde
vite preview is een preview-server. Deze beëindigt geen TLS (transport layer security) en beschikt niet over eigen toegangscontrole; plaats deze daarom achter nginx of Caddy zodra de server publiek toegankelijk is.
server {
listen 443 ssl;
server_name halcyon.example.com;
location / {
proxy_pass http://127.0.0.1:1420;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Een domeinnaam voor de container vereist een extra instelling. Halcyon reageert op localhost, ruwe IP-adressen en de namen van de machine waarop het draait, als beveiliging tegen DNS-rebinding. Binnen een container is de machine waarop het draait de container zelf, dus de hostname is niet die van u. Een verzoek dat binnenkomt als halcyon.example.com wordt geweigerd, en de respons toont de hostnaam die werd geweigerd. Voeg die naam toe.
docker run -d --name halcyon -p 127.0.0.1:1420:1420 --restart unless-stopped \
-e HALCYON_ALLOWED_HOSTS=halcyon.example.com \
ghcr.io/halcyon-video/halcyon-videoDe waarde is door komma's gescheiden; een voorafgaande punt zoals .example.com matcht subdomeinen, en all schakelt de controle uit. Gebruik all alleen op een machine die vanaf buitenaf niet bereikbaar is.
Zodra de store via https:// wordt geserveerd, moet het Jellyfin-adres dat u bij het inloggen invoert ook https:// zijn. Een browser blokkeert een platte http:// API-aanroep die wordt gedaan vanaf een HTTPS-pagina, en de console toont Mixed Content: The page at 'https://halcyon.example.com/' was loaded over HTTPS, but requested an insecure resource. Het inloggen mislukt simpelweg, zonder uitleg binnen Halcyon. Serveer beide via TLS, of houd beide op platte HTTP binnen een privénetwerk.
Vervolgens authenticatie. De store vraagt om Jellyfin-inloggegevens, dus een vreemde die de URL vindt, krijgt een inlogscherm te zien. Eén functie verandert dat. Door Remote Play in te schakelen onder Settings en vervolgens Connection, stelt u uw Jellyfin-sessie beschikbaar aan de server, zodat bezoekers van /remote.html hun eigen instantie van uw echte bibliotheek krijgen. Dat is het doel van deze functie, en het betekent dat de geheimhouding van de URL de enige barrière is tussen het internet en uw films. Als u Remote Play inschakelt, plaats dan single sign-on voor de gehele site met Authentik als een self-hosted SSO-gateway, of laat de publieke hostnaam vallen en benader de store via een WireGuard-tunnel beheerd met wg-easy.
Twee details zijn hierbij van belang. De reverse proxy transporteert alleen de store: de Remote Play-stream is WebRTC over UDP en loopt niet via een HTTP-proxy. Daarom heeft deze een eigen pad nodig op 3478/udp en op 49200 tot 49260/udp wanneer de meegeleverde TURN-relay in gebruik is. En de platte docker run hierboven behoudt geen volume, waardoor de Remote Play-seed niet overleeft na docker rm. Het Compose-bestand koppelt om precies die reden een halcyon-data-volume op /data en stelt REMOTE_PLAY_SEED in op /data/remote-play-seed.json.
Wat te doen als de store slecht presteert
Halcyon rendert op aanvraag. Een inactieve store berekent geen frames, en het verliezen van de focus op het venster stopt de animatieloop; dit is de reden waarom een openstaand tabblad de accu van een laptop niet leegtrekt. Dit helpt bij een machine die net aan de eisen voldoet. Het doet echter niets voor een machine die de store helemaal niet kan tekenen.
Voor dergelijke clients is er een 2.5D-modus, bestaande uit standaard HTML en CSS zonder WebGL, bedoeld voor hardware zo klein als een Raspberry Pi. U schakelt tussen 3D en 2.5D via de instellingen of het powermenu zonder de pagina te verversen, waardoor het testen van beide op hetzelfde apparaat slechts seconden duurt. Wees realistisch over het resultaat: de auteur omschrijft de platte modus als ruw en nog in ontwikkeling. Beschouw dit als een fallback voor zwakke clients.
Wanneer een client te beperkt is voor de 3D-store, is de foutmelding duidelijk. Het tabblad ververst zichzelf, of de browser meldt een verloren WebGL-context, meestal terwijl de schappen nog worden gevuld. Zet dat apparaat over naar 2.5D in plaats van uw bibliotheek in te korten.
Pin de image en controleer deze voordat u een pull uitvoert
Neem dit onderdeel serieus. Tags v0.1.0 tot en met v0.3.1 verschenen allemaal binnen enkele dagen na elkaar, en v0.2.1 bestaat alleen omdat de image-push voor v0.2.0 mislukte. Het tempo is sindsdien niet vertraagd: v0.25.0 werd gepubliceerd op 7 oktober 2026, drie dagen na v0.23.0. Bugrapporten zijn welkom bij de upstream-ontwikkelaar, patches niet; de release-stream is dus de actuele werkstatus van één persoon.
Het draaien van latest met de gewoonte om docker pull te gebruiken, betekent dat de store op elke willekeurige dinsdag onder uw voeten kan veranderen. Pin de image op basis van de digest, de enige referentie die niet kan wijzigen.
docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.25.0Dit commando toont de digest achter de tag. Gebruik deze in plaats van de tag.
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-video@sha256:7fc00e619a30004d275faa79ad36d810e5a8530757132603065ba4e848dd67deDeze digest was 0.25.0 op 9 oktober 2026. Lees de huidige digest zelf in plaats van deze te kopiëren en lees de release notes voordat u een update uitvoert, omdat een patch-release hier zowel wijzigingen in de store-layout als fixes kan bevatten.
FAQ
Heeft Halcyon een GPU nodig op mijn VPS?
Niet voor normaal gebruik. De winkel wordt door three.js in de browser getekend, dus de client-machine verzorgt de rendering en de container serveert enkel statische bestanden op poort 1420. De uitzondering is Remote Play, dat headless Chromium op de server draait en het resultaat streamt. Dat pad rendert op de CPU, tenzij u /dev/dri toewijst aan de container voor hardwareversnelling.
Kan ik Halcyon op het openbare internet plaatsen?
Alleen achter authenticatie. De winkel vraagt om Jellyfin-inloggegevens, maar het inschakelen van Remote Play stelt uw Jellyfin-sessie beschikbaar aan de server. Iedereen die /remote.html laadt, krijgt dus een instantie van uw echte bibliotheek zonder in te loggen. Plaats een reverse proxy met single sign-on voor de applicatie, of houd de hostnaam buiten de openbare DNS en benader de winkel via een VPN.
Waarom zijn de schappen leeg nadat ik ben ingelogd?
De browser roept de Jellyfin API rechtstreeks aan; Jellyfin moet daarom bereikbaar zijn vanuit de browser en niet alleen vanaf de VPS. Open de browserconsole. blocked by CORS policy betekent dat Jellyfin het verzoek vanaf het adres van Halcyon niet accepteert. Een Mixed Content-melding betekent dat de pagina op HTTPS staat, terwijl het door u ingevoerde Jellyfin-adres plain HTTP is.
Heb ik --network host nodig?
Alleen voor Remote Play. WebRTC moet het echte adres van de machine adverteren, en achter de Docker-bridge kan de container alleen een 172.x-adres aanbieden dat geen enkel apparaat op uw netwerk kan bereiken. Voor het browsen door de winkel in een browser volstaat -p 1420:1420, wat aanzienlijk minder van de host blootstelt.
Welke image-tag moet ik gebruiken?
Pin een digest in plaats van latest. Lees de digest voor een versie met docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.25.0, voer die digest uit en stap pas over na het lezen van de release notes. Sinds 9 oktober 2026 bevat de gepubliceerde image zowel linux/amd64 als linux/arm64, waardoor een arm64-host deze als elke andere image ophaalt en niet vanaf de clone hoeft te bouwen.