SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-31

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 idinidisenyo 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 case sa shelf. Naglalakad ka sa mga aisle sa ilalim ng strip lights, kumukuha ng box, binabaligtad ito para basahin ang specs sa likod, at dinadala ito sa counter upang simulan ang playback. Ipinapadala pabalik ng playback ang start, progress, at stop report sa Jellyfin, kaya nananatiling tama ang resume points at watch history.

Binabasa ng Halcyon ang isang umiiral na Jellyfin server gamit ang Jellyfin API at wala itong sariling library. Ipinapalagay ng guide na tumatakbo na nang maayos ang Jellyfin at malinis nitong sini-scan ang library. Kung hindi pa, i-set up muna ang Jellyfin bilang media server sa VPS at bumalik kapag tama na ang hitsura ng iyong library sa normal na web client. Ito ang uri ng software na ini-install dahil mayroon na ang library, hindi dahil kailangan mo ng isa pang serbisyo sa iyong self-hosting list.

GPL-3.0 ang lisensya ng project at isang tao lamang ang sumulat nito. Malinaw ding sinasabi sa README na hindi ito tumatanggap ng pull request. Mabilis ang development at walang pangalawang maintainer na makakahuli ng regression, kaya i-pin ang image version bago mo ipakita ang store sa iba. Tinalakay kung paano ito gawin sa huling seksyon.

Saan nagaganap ang rendering?

Sa browser. Ang Halcyon ay isang Vite at TypeScript app na binuo gamit ang three.js, isang JavaScript library na nagdi-draw ng 3D graphics sa pamamagitan ng WebGL (web graphics library, ang interface ng browser papunta sa GPU). Ang geometry ng store at ang box art ay pinagsasama ng machine na nakakonekta sa screen.

Kaunti lang ang ginagawa ng container. Pinapatakbo nito ang npm run serve, na vite preview --port 1420 --strictPort --host, at sine-serve ang mga built file pati ang ilang maliliit na middleware route. Walang transcoding na idinadagdag ang Halcyon at walang engine na pinapatakbo sa server.

Kaya sa client nauukol ang tanong tungkol sa GPU. Maayos itong patatakbuhin ng maliit na VPS dahil ang pag-serve nito ay nangangahulugan lamang ng paghahatid ng static files sa HTTP. Ang laptop, tablet, o television na nagpapatakbo ng browser ang nagtatakda kung magiging maayos ang galaw ng store o mabagal ito.

May isang feature na lumilihis sa patakarang ito. Naglulunsad ang Remote Play ng mga headless Chromium instance sa server at sini-stream ang na-render na store papunta sa phone o set top box sa pamamagitan ng WebRTC (web real time communication). Sa path na ito, sa server nagaganap ang rendering. Dalawang instance ang default na limitasyon 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 tindahan mula sa iyong library

Mula sa sariling structure ng Jellyfin ang mga aisle. Inaayos ng Halcyon ang mga seksyon batay sa iyong mga library at genre, at pinapangkat nito ang mga sequel mula sa iyong BoxSets. Ang mga specification na naka-print sa likod ng bawat case ay galing sa MediaStreams metadata na hawak na ng Jellyfin. Ibig sabihin, anumang wala sa Jellyfin ay wala rin sa shelf.

Dahil dito, tumpak na salamin ng iyong metadata ang tindahan. Mas maayos ang hitsura rito ng library na pinapakain ng isang arr stack sa Docker Compose na kumpleto na ang artwork at genre kaysa sa isang folder ng magkakahiwalay na file na generic ang mga pangalan. Nakadepende rin ang mga photo library sa software na nag-index sa mga ito. Mahalagang tandaan ito kapag pinag-iisipan mo ang PhotoPrism kumpara sa Immich para sa mga still image na nasa parehong server.

Bago mag-install ng anuman, subukan ang demo ng video store

Ipinapakita ng project ang buong store na tumatakbo gamit ang synthetic library sa naka-host na demo. Kapag idinagdag ang ?demo=1 sa anumang Halcyon URL, ganoon din ang resulta 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 nagso-stutter ang demo sa device na gagamitin mo sa pag-browse, magso-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 nakadokumento sa upstream.

docker run -d --name halcyon --network host --restart unless-stopped \
  ghcr.io/halcyon-video/halcyon-video

Pagkatapos, tingnan kung matagumpay itong nagsimula.

docker logs halcyon
curl -I http://127.0.0.1:1420

Dapat ipakita ng 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 sinusubukan ng server na lumipat 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 totoong address ng machine sa device na hihingi ng stream. Sa likod ng default Docker bridge, sarili nitong 172.x address lamang ang alam ng container. Hindi ito maaabot ng teleponong nakakonekta sa network mo, kaya hindi kailanman kumokonekta ang stream. Kung store lang ang gusto mong gamitin sa browser, i-publish na lang ang port.

docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
  ghcr.io/halcyon-video/halcyon-video

Mas mainam itong default sa isang VPS dahil inilalagay ng host networking ang container sa bawat interface ng machine, kabilang 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, na kapareho ng ideya ng Compose services na awtomatikong nagsisimula sa boot.

Sa pag-clone ng repository at pagpapatakbo ng docker compose up -d, lokal na binubuo ang image. Bilang default, nagbu-build mula sa source ang naka-commit na Compose file at naka-comment out ang prebuilt na image: line. Alisin ang comment sa linyang iyon kung gusto mong gamitin sa Compose ang published image.

May isang mahalagang limitasyon hanggang August 2026: linux/amd64 lamang ang architecture ng 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 na may 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 ang mga variable na may prefix na VITE_ sa client-side code, kaya ang Jellyfin password na inilagay roon ay naisasa-compile sa JavaScript bundle na dina-download ng bawat bisita. 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 simulan ang pag-debug.

Una, dapat maabot ang Jellyfin mula sa browser, hindi lamang mula sa VPS na nagse-serve sa Halcyon. Ayos ang Jellyfin na naka-bind sa 127.0.0.1:8096 para sa local test, pero magiging walang laman ang mga shelf para sa lahat ng iba.

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 pinahigpit 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

vite preview ay isang preview server. Hindi ito nagte-terminate ng TLS (transport layer security) at wala itong sariling access control, kaya dapat itong nasa likod ng nginx o Caddy kapag public ang access.

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. Sumasagot ang Halcyon sa localhost, raw IP address, at mga pangalan ng machine na pinapatakbo nito, bilang proteksiyon laban sa DNS rebinding. Sa loob ng container, ang machine na pinapatakbo nito ay ang container mismo, kaya hindi ang hostname nito ang hostname mo. Tinatanggihan ang request na dumarating bilang halcyon.example.com, at binabanggit sa response ang 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-video

Comma-separated ang value. Ang leading dot gaya ng .example.com ay tumutugma sa mga subdomain, at pinapatay ng all ang check. Gamitin lamang ang all sa machine na walang external na makakaabot.

Kapag sine-serve ang store sa https://, dapat https:// rin ang Jellyfin address na inilalagay mo sa login. Bina-block 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. I-serve pareho gamit ang TLS, o panatilihing plain HTTP ang pareho sa loob ng private network.

Sunod ang authentication. Humihingi ang store ng Jellyfin credentials, kaya login screen ang makikita ng sinumang makahanap ng URL. May isang feature na nagbabago nito. Kapag in-enable ang Remote Play sa Settings at pagkatapos ay Connection, ipinapasa nito sa server ang Jellyfin session mo. Dahil dito, ang mga bumibisita sa /remote.html ay nakakakuha ng sariling instance ng aktuwal mong library. Iyan ang layunin ng feature, at nangangahulugan itong ang pagiging lihim ng URL ang nagsisilbing hadlang 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 dinadala. Ang Remote Play stream ay WebRTC sa UDP at hindi dumaraan 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 ito.

Ano ang gagawin kapag mabagal o hindi maayos ang store

On demand ang pag-render ng Halcyon. Kapag idle ang store, wala itong kino-composite na frame. Kapag nawalan ng focus ang window, hihinto ang animation loop. Kaya hindi nauubos ang baterya ng laptop kahit iwanang bukas ang tab. Makakatulong ito sa machine na nasa hangganan lang ng kaya nitong performance. 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 lang ito at walang WebGL. Dinisenyo ito para sa hardware na kasingliit ng Raspberry Pi. Maaaring 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 pagsubok sa dalawang mode sa parehong device. Maging makatotohanan sa inaasahang resulta. Inilalarawan ng author ang flat mode bilang magaspang pa at patuloy pang ginagawa. Ituring ito bilang fallback para sa mahihinang client.

Kapag masyadong mahina ang client para sa 3D store, kapansin-pansin agad ang failure. Kusang nagre-reload ang tab, o nag-uulat ang browser na nawala ang WebGL context, karaniwan habang pinupuno pa ang mga shelf. Ilipat ang device na iyon sa 2.5D sa halip na bawasan ang library mo.

Image i-pin, at i-check muna bago mag-pull

Seryosohin ang bahaging ito. Ang mga tag na v0.1.0 hanggang v0.3.1 ay na-release sa loob ng magkakasunod na araw, at umiiral lang 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 nakabatay sa working state ng isang tao.

Kapag pinapatakbo ang latest na may nakasanayang docker pull, maaaring magbago ang store habang ginagamit mo ito sa isang ordinaryong Martes. I-pin gamit ang digest, ang tanging reference na hindi maaaring magbago.

docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.3.1

Ipinapakita nito ang digest sa likod ng tag. Gamitin ito sa halip na tag.

docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
  ghcr.io/halcyon-video/halcyon-video@sha256:747dcc821a3d2fa318b50e76024783c1835609047e84f502e23d021bc1898b20

Ang 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 maglaman ang isang patch release dito ng mga pagbabago sa store layout bukod sa mga fix.

FAQ

Kailangan ba ng GPU ang Halcyon sa VPS ko?

Hindi para sa karaniwang paggamit. Iginuguhit ang store ng three.js sa browser, kaya ang client machine ang gumagawa ng rendering at static files lamang sa port 1420 ang inihahatid ng container. Ang exception ay 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 imamapa 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 na-enable ang Remote Play, ipinapasa sa server ang iyong Jellyfin session. Dahil dito, makakakuha ang sinumang mag-load ng /remote.html ng instance ng aktuwal mong library nang hindi nagla-login. Maglagay ng reverse proxy na may single sign-on sa unahan nito, o huwag ilagay ang hostname sa public DNS at i-access ang store sa pamamagitan ng VPN.

Bakit walang laman ang shelves pagkatapos kong mag-login?

Direktang tumatawag ang browser sa Jellyfin API, kaya dapat maabot ng browser ang Jellyfin at hindi lamang ng 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, pero 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 totoong 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 maliit ang bahaging 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 bersyong may docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.3.1, gamitin ang digest na iyon, at mag-update lamang pagkatapos 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.