Halcyon: Gawing 90s Video Store ang Jellyfin
Gawing nalalakaran sa browser ang Jellyfin library gamit ang Halcyon. Alamin ang Docker command, reverse proxy setup, at mga limitasyong dapat asahan.
Ano ang ginagawa ng Halcyon sa iyong Jellyfin library
Muling iginuguhit ng Halcyon Video ang iyong Jellyfin library bilang isang video store noong 1990s na maaari mong lakaran sa browser. Ang bawat pelikulang pagmamay-ari mo ay nagiging isang kahon sa shelf. Naglalakad ka sa mga aisle sa ilalim ng strip lights, kumukuha ng kahon, binabaligtad ito para basahin ang specs sa likod, at dinadala ito sa counter upang simulan ang playback. Ipinapadala ng playback sa Jellyfin ang impormasyon tungkol sa pagsisimula, progreso, at paghinto, kaya nananatiling tama ang resume points at watch history.
Binabasa ng Halcyon ang isang umiiral na Jellyfin server sa pamamagitan ng Jellyfin API at wala itong sariling library. Ipinapalagay ng gabay na ito na tumatakbo na ang Jellyfin at maayos itong nag-scan. Kung hindi, i-set up muna ang Jellyfin bilang media server sa isang VPS at bumalik kapag tama na ang itsura ng iyong library sa karaniwang web client. Ito ang uri ng app na ini-install dahil naroon na ang library, hindi dahil kailangan mo ng isa pang service sa iyong self-hosting list.
GPL-3.0 ang lisensya ng proyekto at isang tao lamang ang sumulat nito. Malinaw ding nakasaad sa README na hindi ito tumatanggap ng pull requests. Mabilis ang development at walang pangalawang maintainer na makakahuli ng regression, kaya i-pin ang image version bago mo ipakita ang store sa iba. Tatalakayin ng huling seksyon kung paano.
Saan isinasagawa ang rendering?
Sa browser. Ang Halcyon ay isang Vite at TypeScript app na binuo gamit ang three.js, isang JavaScript library na nagdo-draw ng 3D graphics sa pamamagitan ng WebGL (web graphics library, ang interface ng browser para sa GPU). Ang store geometry at box art ay kino-composite ng machine na nakakonekta sa screen.
Kaunti lamang ang ginagawa ng container. Pinapatakbo nito ang npm run serve, na vite preview --port 1420 --strictPort --host, at sine-serve ang mga built file kasama ang ilang maliliit na middleware route. Walang transcoding ang Halcyon at wala itong engine na pinapatakbo sa server.
Kaya sa client nakasalalay ang GPU question. Maayos itong mase-serve ng isang maliit na VPS dahil static files lamang ang sine-serve nito sa HTTP. Ang laptop, tablet, o television na nagpapatakbo ng browser ang nagpapasya kung maayos na gagalaw ang store o mabagal itong magre-render.
May isang feature na lumilihis sa patakarang ito. Nagse-spawn ang Remote Play ng headless Chromium instances sa server at ini-stream ang rendered store sa phone o set top box gamit ang WebRTC (web real time communication). Sa path na ito, sa server isinasagawa ang rendering. Dalawang instance ang default na limit at maaaring baguhin gamit ang REMOTE_PLAY_MAX_INSTANCES. Kung walang naka-map na /dev/dri device, sa CPU nagre-render ang mga instance na iyon. Dahil dito, ramdam ng isang two core VPS ang bawat karagdagang viewer.
Mga binabasa ng store mula sa iyong library
Ang mga aisle ay nagmumula sa sariling structure ng Jellyfin. Inaayos ng Halcyon ang mga section batay sa iyong libraries at genres, at pinapangkat nito ang mga sequel mula sa iyong BoxSets. Ang specs na naka-print sa likod ng bawat case ay nagmumula sa MediaStreams metadata na mayroon na sa Jellyfin. Ibig sabihin, anumang wala sa Jellyfin ay wala rin sa shelf.
Dahil dito, tapat na repleksyon ng iyong metadata ang store. Mas maayos ang itsura rito ng library na pinapakain ng isang arr stack sa Docker Compose na mayroon nang artwork at genres kaysa sa isang folder ng magkakahiwalay na file na may generic na mga pangalan.
Subukan ang demo ng video store bago mag-install ng anuman
Inilalabas ng proyekto ang buong store na tumatakbo gamit ang synthetic library sa hosted demo. Kapag idinagdag ang ?demo=1 sa anumang Halcyon URL, ganoon din ang mangyayari sa sarili mong deployment.
Gamitin ito bilang hardware test. May humigit-kumulang 2,000 title ang demo library at nangangailangan ng tinatayang 2 GB ng browser memory, kaya mas mabigat ito kaysa sa karamihan ng personal library. Kung nagkaka-stutter ang demo sa device na gagamitin mong pang-browse, mag-stutter din ang sarili mong library. Ang dapat na ayusin ay ang 2.5D mode na inilalarawan sa ibaba, hindi ang paglipat sa mas malaking VPS.
Patakbuhin ito gamit ang Docker
Ito ang command na idinokumento ng upstream.
docker run -d --name halcyon --network host --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoPagkatapos, tingnan kung maayos itong nagsimula.
docker logs halcyon
curl -I http://127.0.0.1:1420Dapat ipakita sa log na nakikinig ang preview server sa port 1420, at dapat sumagot ang curl sa HTTP/1.1 200 OK. Ang container na lumalabas sa loob ng ilang segundo ay halos palaging may problema sa port. Ibig sabihin ng --strictPort, hindi maaaring awtomatikong lumipat ang server sa 1421 kapag ginagamit na ang 1420, kaya humihinto ito.
Para sa Remote Play ang --network host, hindi para sa store. Kailangang i-advertise ng WebRTC ang aktuwal na address ng machine sa device na hihingi ng stream. Sa default na Docker bridge, sarili lamang nitong 172.x address ang alam ng container. Hindi ito maaabot ng anumang phone sa network mo, kaya hindi kumokonekta ang stream. Kung browser lang ang gagamitin mo para sa store, i-publish na lang ang port.
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoMas magandang default ito sa isang VPS dahil inilalagay ng host networking ang container sa lahat ng interface ng machine, kasama ang public interface. Tinalakay sa Pagpapatakbo ng Docker sa isang VPS ang iba pang bahagi ng trade-off na ito. Ang --restart unless-stopped ang nagbabalik sa store pagkatapos ng reboot, kapareho ng ideya sa Mga Compose service na awtomatikong nagsisimula sa boot.
Sa halip, ang pag-clone ng repository at pagpapatakbo ng docker compose up -d ay nagbu-build ng image nang lokal. Bilang default, nagbu-build mula sa source ang naka-commit na Compose file at may naka-comment na prebuilt na linyang image:. Alisin ang comment sa linyang iyon kung gusto mong gamitin sa Compose ang published image.
May isang mahalagang limitasyon hanggang Agosto 2026: linux/amd64 lang ang published image. Nabigo sa emulation ang arm64 na bahagi ng multi architecture push at naghihintay ito ng native arm runners. Sa arm64 VPS, mabibigo ang pull gamit ang no matching manifest for linux/arm64/v8 in the manifest list entries. Ang pagbuo mula sa clone ang paraan upang malampasan ito.
Ituro ito sa iyong Jellyfin server
Buksan ang http://<host>:1420 at mag-log in gamit ang address, username, at password ng iyong Jellyfin server. Para lamang sa local development ang .env.local.example file sa repository. Inilalantad ng Vite sa client-side code ang mga variable na may prefix na VITE_, kaya ang Jellyfin password na inilagay doon ay kino-compile sa JavaScript bundle na dina-download ng bawat visitor. Sa server na maaaring ma-access ng ibang tao, mag-log in sa pamamagitan ng interface.
Direktang kumokonekta ang browser sa Jellyfin. Hindi ipinapasa ng container ng Halcyon ang Jellyfin API, at may dalawang epekto ito na dapat mong malaman bago magsimulang mag-debug.
Una, dapat maabot ng browser ang Jellyfin, hindi lamang ang VPS na nagsisilbi sa Halcyon. Ang Jellyfin na naka-bind sa 127.0.0.1:8096 ay angkop para sa local test, pero magiging walang laman ang mga shelf para sa lahat ng ibang user.
Ikalawa, cross-origin ang request, mula sa address ng Halcyon papunta sa address ng Jellyfin. Bilang default, sinasagot ng Jellyfin ang mga API request gamit ang Access-Control-Allow-Origin: *, kaya gumagana ito nang walang karagdagang configuration. Kung pinaghigpitan mo ang setting na iyon, o naglagay ka ng authentication proxy sa harap ng Jellyfin API, iuulat ng browser console ang blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource at maglo-load ang store na walang laman ang mga shelf.
Ilagay ito sa likod ng reverse proxy, na may authentication sa unahan
Ang vite preview ay isang preview server. Hindi ito nagte-terminate ng TLS (transport layer security) at wala itong sariling access control, kaya dapat itong ilagay sa likod ng nginx o Caddy kapag public ang deployment.
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;
}
}Kailangan ng isa pang setting kapag may domain name sa harap ng container. Tumatanggap ang Halcyon ng localhost, mga raw IP address, at mga pangalan ng machine na pinapatakbuhan nito bilang proteksiyon laban sa DNS rebinding. Sa loob ng container, ang machine na pinapatakbuhan nito ay ang container mismo, kaya hindi nito ginagamit ang hostname mo. Tinatanggihan ang request na dumarating bilang halcyon.example.com, at inilalagay sa response ang pangalan ng host na tinanggihan nito. Idagdag ang pangalang iyon.
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-videoComma-separated ang value. Ang nauunang tuldok gaya ng .example.com ay tumutugma sa mga subdomain, at pinapatay ng all ang check. Gamitin lamang ang all sa machine na walang makakaabot mula sa labas.
Kapag inihahain ang store sa https://, dapat https:// din ang Jellyfin address na inilalagay mo sa login. Hinaharang ng browser ang plain http:// API call mula sa HTTPS page, at ipinapakita ng console ang Mixed Content: The page at 'https://halcyon.example.com/' was loaded over HTTPS, but requested an insecure resource. Basta nabibigo ang login, nang walang paliwanag sa loob ng Halcyon. Ihain ang dalawa sa TLS, o panatilihing plain HTTP ang dalawa sa loob ng private network.
Sunod ang authentication. Humihingi ang store ng Jellyfin credentials, kaya login screen ang makikita ng estrangherong makahanap sa URL. May isang feature na nagbabago nito. Kapag in-on ang Remote Play sa Settings at pagkatapos ay Connection, ipinapasa nito ang Jellyfin session mo sa server upang magkaroon ang mga bumibisita sa /remote.html ng sarili nilang instance ng aktuwal mong library. Iyan ang layunin ng feature, at nangangahulugan itong ang pagiging lihim ng URL ang tanging harang sa pagitan ng internet at ng mga pelikula mo. Kung ie-enable mo ang Remote Play, maglagay ng single sign on sa harap ng buong site gamit ang Authentik bilang self-hosted SSO gateway, o alisin ang public hostname at i-access ang store sa pamamagitan ng WireGuard tunnel na pinamamahalaan gamit ang wg-easy.
May dalawang kaugnay na detalye. Ang reverse proxy ay store lamang ang dinaraanan: ang Remote Play stream ay WebRTC sa UDP at hindi dumadaan sa HTTP proxy, kaya kailangan nito ng sariling path sa 3478/udp at sa 49200 hanggang 49260/udp kapag ginagamit ang bundled TURN relay. Wala ring volume ang plain docker run sa itaas, kaya hindi nananatili ang Remote Play seed pagkatapos ng docker rm. Nagmo-mount ang Compose file ng halcyon-data volume sa /data at itinatakda ang REMOTE_PLAY_SEED sa /data/remote-play-seed.json para sa eksaktong dahilang iyon.
Ano ang gagawin kapag mabagal o hindi maayos ang store
Nagre-render ang Halcyon kapag kinakailangan. Kapag idle ang store, wala itong kino-composite na frame. Kapag nawalan ng focus ang window, hihinto ang animation loop. Kaya hindi nauubos agad ang baterya ng laptop kahit may tab na nakabukas. Nakakatulong ito sa machine na halos sapat lang ang kakayahan. Wala itong magagawa sa machine na hindi kayang i-render ang store.
Para sa mga client na iyon, may 2.5D mode. Plain HTML at CSS lamang ito at walang WebGL. Dinisenyo ito para gumana kahit sa hardware na kasing-liit ng Raspberry Pi. Maaari kang lumipat sa pagitan ng 3D at 2.5D mula sa settings o power menu nang hindi nire-reload ang page. Kaya ilang segundo lang ang kailangan para subukan ang dalawang mode sa parehong device. Maging makatotohanan sa resulta. Inilalarawan ng author ang flat mode bilang magaspang at patuloy pang ginagawa. Gamitin ito bilang fallback para sa mahihinang client.
Kapag masyadong mahina ang client para sa 3D store, kapansin-pansin ang failure. Kusang nagre-reload ang tab, o nag-uulat ang browser ng nawawalang WebGL context, karaniwan habang pinupuno pa ang mga shelf. Ilipat ang device na iyon sa 2.5D sa halip na bawasan ang library mo.
I-pin ang image, at mag-check bago mag-pull
Seryosohin ang bahaging ito. Ang mga tag na v0.1.0 hanggang v0.3.1 ay inilabas sa pagitan lamang ng ilang araw, at umiiral lamang ang v0.2.1 dahil nabigo ang image push para sa v0.2.0. Tinatanggap ang mga bug report sa upstream, pero hindi ang mga patch, kaya ang release stream ay working state ng isang tao.
Kapag pinapatakbo ang latest na nakasanayang docker pull, maaaring magbago ang store nang walang abiso sa karaniwang araw. Mag-pin gamit ang digest, ang tanging reference na hindi maaaring magbago.
docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.3.1Ipinapakita nito ang digest sa likod ng tag. Gamitin ito kapalit ng tag.
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-video@sha256:747dcc821a3d2fa318b50e76024783c1835609047e84f502e23d021bc1898b20Ang digest na iyon ay 0.3.1 noong 10 August 2026. Basahin mismo ang kasalukuyang digest sa halip na kopyahin ito, at basahin ang release notes bago mag-update, dahil maaaring may kasamang pagbabago sa store layout ang isang patch release dito bukod sa mga fix.
FAQ
Kailangan ba ng GPU ng Halcyon sa aking VPS?
Hindi para sa karaniwang paggamit. Iginuguhit ng three.js ang store sa browser, kaya ang client machine ang gumagawa ng rendering at static files lamang ang sine-serve ng container sa port 1420. Ang exception ay ang Remote Play, na nagpapatakbo ng headless Chromium sa server at nag-i-stream ng resulta. Sa ganitong setup, CPU ang ginagamit sa rendering maliban kung i-map mo ang /dev/dri sa container para sa hardware acceleration.
Maaari ko bang ilagay ang Halcyon sa public internet?
Tanging kung nasa likod ito ng authentication. Humihingi ang store ng Jellyfin credentials, pero kapag in-enable ang Remote Play, ipinapasa nito ang iyong Jellyfin session sa server. Dahil dito, makakakuha ng instance ng iyong aktuwal na library ang sinumang mag-load ng /remote.html kahit hindi nagla-login. Maglagay ng reverse proxy na may single sign-on sa harap nito, o huwag ilagay ang hostname sa public DNS at i-access ang store sa pamamagitan ng VPN.
Bakit walang laman ang mga shelf pagkatapos kong mag-login?
Direktang tinatawag ng browser ang Jellyfin API, kaya dapat reachable ang Jellyfin mula sa browser at hindi lamang mula sa VPS. Buksan ang browser console. Ibig sabihin ng blocked by CORS policy na hindi tinatanggap ng Jellyfin ang request mula sa address ng Halcyon. Ibig sabihin ng mensaheng Mixed Content na HTTPS ang page habang plain HTTP ang Jellyfin address na inilagay mo.
Kailangan ko ba ang --network host?
Para lamang ito sa Remote Play. Kailangang i-advertise ng WebRTC ang tunay na address ng machine. Sa likod ng Docker bridge, 172.x address lamang ang maiaalok ng container, at hindi ito maaabot ng anumang phone sa iyong network. Para sa pag-browse sa store gamit ang browser, gumagana ang -p 1420:1420 at mas kaunti ang inilalantad nito sa host.
Aling image tag ang dapat kong gamitin?
Mag-pin ng digest sa halip na latest. Basahin ang digest para sa isang version na may docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.3.1, gamitin ang digest na iyon, at mag-update lamang matapos basahin ang release notes. Noong August 2026, linux/amd64 lamang ang published image, kaya kailangang mag-build ng arm64 host mula sa clone gamit ang docker compose up -d.