Halcyon: Ubah Jellyfin Jadi Toko Video Tahun 90-an
Jelajahi pustaka Jellyfin seperti toko rental tahun 1990-an. Panduan ini memuat perintah Docker, reverse proxy, dan batasan Halcyon yang perlu diketahui.
Halcyon untuk pustaka Jellyfin Anda
Halcyon Video menampilkan ulang pustaka Jellyfin Anda sebagai toko video tahun 1990-an yang dapat dijelajahi melalui browser. Setiap film yang Anda miliki menjadi satu sampul pada rak. Anda berjalan di antara lorong-lorong di bawah lampu strip, mengambil kotak, membaliknya untuk membaca spesifikasi di bagian belakang, lalu membawanya ke meja kasir untuk memulai pemutaran. Status pemutaran—mulai, progres, dan berhenti—dikirim kembali ke Jellyfin sehingga titik lanjut dan riwayat tontonan tetap benar.
Halcyon membaca server Jellyfin yang sudah ada melalui Jellyfin API dan tidak menyimpan pustakanya sendiri. Panduan ini mengasumsikan Jellyfin sudah berjalan dan melakukan pemindaian tanpa error. Jika belum, siapkan Jellyfin sebagai server media pada VPS terlebih dahulu, lalu kembali setelah pustaka Anda tampil dengan benar pada web client biasa. Anda memasang aplikasi seperti ini karena pustakanya sudah tersedia, bukan karena Anda membutuhkan service lain dalam daftar self-hosting Anda.
Proyek ini berlisensi GPL-3.0 dan ditulis oleh satu orang. README menyatakan dengan jelas bahwa proyek ini tidak menerima pull request. Pengembangan berlangsung cepat dan tidak ada maintainer kedua yang dapat mendeteksi regresi. Karena itu, tetapkan versi image sebelum Anda memperlihatkan toko tersebut kepada orang lain. Bagian terakhir menjelaskan caranya.
Di mana rendering berlangsung?
Di browser. Halcyon adalah aplikasi Vite dan TypeScript yang dibangun di atas three.js, yaitu library JavaScript yang menggambar grafik 3D melalui WebGL (web graphics library, antarmuka browser ke GPU). Geometri toko dan ilustrasi kemasan dikomposisikan oleh mesin yang terhubung ke layar.
Container hanya menangani sedikit pekerjaan. Container menjalankan npm run serve, yaitu vite preview --port 1420 --strictPort --host, dan menyajikan file hasil build serta beberapa rute middleware kecil. Halcyon tidak melakukan transcoding dan tidak menjalankan engine di server.
Jadi, pertanyaan tentang GPU bergantung pada client. VPS kecil dapat melayani aplikasi ini dengan baik karena tugasnya hanya menyajikan file statis melalui HTTP. Laptop, tablet, atau televisi yang menjalankan browser menentukan apakah toko berjalan lancar atau tersendat.
Satu fitur tidak mengikuti aturan tersebut. Remote Play menjalankan instance Chromium tanpa antarmuka grafis di server dan men-streaming toko yang telah dirender ke ponsel atau set top box melalui WebRTC (web real time communication). Jalur ini melakukan rendering di server. Secara default, jumlahnya dibatasi hingga two instance dan dapat disesuaikan dengan REMOTE_PLAY_MAX_INSTANCES. Tanpa perangkat /dev/dri yang dipetakan, instance tersebut melakukan rendering pada CPU. Akibatnya, VPS dengan two core akan merasakan dampak setiap penonton tambahan.
Yang dibaca toko dari library Anda
Lorong-lorong toko berasal dari struktur Jellyfin. Halcyon menyusun bagian berdasarkan library dan genre Anda, serta mengelompokkan sekuel dari BoxSets. Spesifikasi yang tercetak pada bagian belakang setiap wadah berasal dari metadata MediaStreams yang sudah disimpan Jellyfin. Artinya, data yang tidak ada di Jellyfin juga tidak akan ada di rak.
Dengan demikian, toko ini mencerminkan metadata Anda secara akurat. Library yang diisi oleh arr stack dalam Docker Compose dan sudah dilengkapi artwork serta genre akan terlihat jauh lebih baik di sini daripada folder berisi file terpisah dengan nama generik. Library foto juga bergantung pada komponen yang mengindeksnya. Hal ini perlu diingat saat Anda membandingkan PhotoPrism dengan Immich untuk foto yang disimpan di server yang sama.
Coba demo video store sebelum menginstal apa pun
Proyek ini menyediakan seluruh video store yang berjalan dengan library sintetis di demo yang di-host. Menambahkan ?demo=1 ke URL Halcyon apa pun akan memberikan hasil yang sama pada deployment Anda sendiri.
Gunakan demo ini untuk menguji perangkat keras. Library demo berisi sekitar 2,000 judul dan memerlukan sekitar 2 GB memori browser, sehingga lebih berat daripada sebagian besar library pribadi. Jika demo tersendat pada perangkat yang akan Anda gunakan untuk menjelajah, library Anda sendiri juga akan tersendat. Solusinya adalah mode 2.5D yang dijelaskan di bawah, bukan VPS yang lebih besar.
Jalankan dengan Docker
Berikut adalah perintah yang didokumentasikan oleh upstream.
docker run -d --name halcyon --network host --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoSelanjutnya, periksa apakah layanan berhasil berjalan.
docker logs halcyon
curl -I http://127.0.0.1:1420Log seharusnya menunjukkan bahwa preview server sedang listen pada port 1420, dan curl seharusnya merespons dengan HTTP/1.1 200 OK. Container yang berhenti dalam beberapa detik hampir selalu mengalami masalah port. --strictPort berarti server menolak berpindah ke 1421 ketika 1420 sudah digunakan, sehingga server berhenti.
--network host diperlukan untuk Remote Play, bukan untuk store. WebRTC harus mengiklankan alamat sebenarnya dari mesin kepada perangkat yang ingin menerima stream. Di balik default Docker bridge, container hanya mengetahui alamat 172.x miliknya sendiri. Tidak ada ponsel di jaringan Anda yang dapat menjangkau alamat tersebut, sehingga stream tidak pernah tersambung. Jika Anda hanya ingin menggunakan store di browser, publish port tersebut.
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoIni adalah default yang lebih baik pada VPS karena host networking menempatkan container pada setiap interface yang dimiliki mesin, termasuk interface publik. Menjalankan Docker pada VPS membahas konsekuensi lainnya. --restart unless-stopped yang menjalankan kembali store setelah reboot, dengan konsep yang sama seperti Compose service yang berjalan saat boot.
Cloning repository dan menjalankan docker compose up -d akan membangun image secara lokal. File Compose yang di-commit secara default membangun image dari source dan menyertakan baris image: bawaan dalam keadaan dikomentari. Hapus komentar pada baris tersebut jika Anda ingin menggunakan image yang dipublikasikan melalui Compose.
Satu batasan penting per August 2026: image yang dipublikasikan hanya tersedia untuk linux/amd64. Bagian arm64 dari push multi-arsitektur gagal saat dijalankan melalui emulasi dan masih menunggu arm runner native. Pada VPS arm64, pull gagal dengan no matching manifest for linux/arm64/v8 in the manifest list entries. Membangun image dari hasil clone adalah solusinya.
Arahkan ke server Jellyfin Anda
Buka http://<host>:1420 dan login dengan alamat server Jellyfin, nama pengguna, serta kata sandi Anda. File .env.local.example dalam repositori hanya digunakan untuk pengembangan lokal. Vite mengekspos variabel yang diawali VITE_ ke kode sisi klien. Karena itu, kata sandi Jellyfin yang ditulis di sana akan dikompilasi ke dalam bundel JavaScript yang diunduh setiap pengunjung. Pada server yang dapat dijangkau orang lain, lakukan login melalui antarmuka.
Browser berkomunikasi langsung dengan Jellyfin. Container Halcyon tidak mem-proxy API Jellyfin. Hal ini memiliki dua konsekuensi yang perlu diketahui sebelum Anda mulai melakukan debugging.
Pertama, Jellyfin harus dapat dijangkau dari browser, bukan hanya dari VPS yang menyajikan Halcyon. Jellyfin yang terikat ke 127.0.0.1:8096 cocok untuk pengujian lokal, tetapi rak akan kosong bagi pengguna lain.
Kedua, panggilan tersebut bersifat cross-origin, dari alamat Halcyon ke alamat Jellyfin. Secara default, Jellyfin menjawab permintaan API dengan Access-Control-Allow-Origin: *, sehingga dapat berfungsi tanpa konfigurasi tambahan. Jika Anda mempersempit pengaturan tersebut atau menempatkan proxy autentikasi di depan API Jellyfin, konsol browser akan melaporkan blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource dan store akan dimuat dengan rak kosong.
Tempatkan di balik reverse proxy, dengan autentikasi di depannya
vite preview adalah server pratinjau. Server ini tidak melakukan TLS termination (transport layer security) dan tidak memiliki kontrol akses sendiri, sehingga untuk layanan publik server ini harus ditempatkan di balik nginx atau Caddy.
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;
}
}Domain yang ditempatkan di depan container memerlukan satu pengaturan tambahan. Halcyon menerima localhost, alamat IP langsung, dan nama mesin tempatnya berjalan sebagai perlindungan terhadap DNS rebinding. Di dalam container, mesin tempatnya berjalan adalah container tersebut, sehingga hostname-nya bukan hostname Anda. Permintaan yang tiba sebagai halcyon.example.com akan ditolak, dan respons menyebutkan host yang ditolaknya. Tambahkan nama tersebut.
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-videoNilainya dipisahkan dengan koma. Titik di awal seperti .example.com cocok dengan subdomain, sedangkan all menonaktifkan pemeriksaan. Gunakan all hanya pada mesin yang tidak dapat dijangkau dari luar.
Setelah store disajikan melalui https://, alamat Jellyfin yang Anda masukkan saat login juga harus berupa https://. Browser memblokir pemanggilan API http:// biasa dari halaman HTTPS, dan console menampilkan Mixed Content: The page at 'https://halcyon.example.com/' was loaded over HTTPS, but requested an insecure resource. Login gagal begitu saja, tanpa penjelasan di dalam Halcyon. Sajikan keduanya melalui TLS, atau gunakan HTTP biasa untuk keduanya di dalam jaringan privat.
Berikutnya, autentikasi. Store meminta kredensial Jellyfin, sehingga orang asing yang menemukan URL akan melihat layar login. Satu fitur mengubah kondisi ini. Mengaktifkan Remote Play, di bawah Settings lalu Connection, meneruskan sesi Jellyfin Anda ke server sehingga pengunjung /remote.html mendapatkan instance mereka sendiri dari library Anda yang sebenarnya. Itulah tujuan fitur ini, dan artinya kerahasiaan URL menjadi satu-satunya penghalang antara Internet dan film Anda. Jika mengaktifkan Remote Play, tempatkan single sign-on di depan seluruh situs dengan Authentik sebagai gateway SSO yang di-host sendiri, atau hapus hostname publik dan akses store melalui tunnel WireGuard yang dikelola dengan wg-easy.
Ada dua detail yang terkait. Reverse proxy hanya meneruskan store. Stream Remote Play menggunakan WebRTC melalui UDP dan tidak melewati proxy HTTP, sehingga memerlukan jalurnya sendiri pada 3478/udp dan 49200 hingga 49260/udp ketika relay TURN bawaan digunakan. Selain itu, docker run biasa di atas tidak menyimpan volume apa pun, sehingga seed Remote Play tidak bertahan setelah docker rm. File Compose memasang volume halcyon-data pada /data dan menetapkan REMOTE_PLAY_SEED ke /data/remote-play-seed.json karena alasan tersebut.
Apa yang harus dilakukan jika store berjalan buruk
Halcyon melakukan rendering sesuai permintaan. Store yang tidak aktif tidak menyusun frame apa pun, dan hilangnya fokus jendela menghentikan loop animasi. Karena itu, tab yang dibiarkan terbuka tidak menguras baterai laptop. Hal ini membantu perangkat yang hanya sedikit di bawah spesifikasi yang diperlukan. Namun, hal ini tidak membantu perangkat yang sama sekali tidak mampu merender store.
Untuk client tersebut, tersedia mode 2.5D berupa HTML dan CSS biasa tanpa WebGL. Mode ini ditujukan untuk perangkat dengan spesifikasi serendah Raspberry Pi. Anda dapat beralih antara mode 3D dan 2.5D melalui pengaturan atau menu daya tanpa memuat ulang halaman. Dengan demikian, pengujian kedua mode pada perangkat yang sama hanya memerlukan beberapa detik. Tetapkan ekspektasi yang realistis. Penulis menjelaskan bahwa mode datar ini masih kasar dan dalam pengembangan. Gunakan mode ini sebagai fallback untuk client dengan kemampuan terbatas.
Jika client terlalu lemah untuk menjalankan store 3D, kegagalannya terlihat jelas. Tab memuat ulang sendiri, atau browser melaporkan bahwa konteks WebGL hilang, biasanya saat rak masih diisi. Alihkan perangkat tersebut ke mode 2.5D, bukan mengurangi library Anda.
Sematkan image, lalu periksa sebelum melakukan pull
Perhatikan bagian ini dengan serius. Tag v0.1.0 hingga v0.3.1 dirilis dalam rentang beberapa hari, dan v0.2.1 hanya ada karena push image untuk v0.2.0 gagal. Laporan bug diterima oleh upstream, tetapi patch tidak diterima, sehingga alur rilis mencerminkan kondisi kerja satu orang.
Menjalankan latest dengan kebiasaan docker pull berarti isi store dapat berubah tanpa pemberitahuan pada hari biasa. Gunakan digest sebagai pin, karena hanya referensi ini yang tidak dapat berubah.
docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.3.1Perintah tersebut menampilkan digest di balik tag. Gunakan digest itu sebagai pengganti tag.
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-video@sha256:747dcc821a3d2fa318b50e76024783c1835609047e84f502e23d021bc1898b20Digest tersebut adalah 0.3.1 pada 10 August 2026. Baca nilai yang terbaru sendiri, jangan menyalinnya, dan baca catatan rilis sebelum melakukan perubahan, karena patch release di sini dapat membawa perubahan tata letak store selain perbaikan.
FAQ
Apakah Halcyon memerlukan GPU pada VPS saya?
Tidak untuk penggunaan normal. Store dirender oleh three.js di browser, sehingga mesin klien yang melakukan rendering dan container hanya menyajikan file statis pada port 1420. Pengecualiannya adalah Remote Play, yang menjalankan Chromium tanpa antarmuka grafis di server dan melakukan streaming hasilnya. Jalur tersebut merender menggunakan CPU, kecuali Anda memetakan /dev/dri ke dalam container untuk akselerasi perangkat keras.
Apakah Halcyon dapat ditempatkan di Internet publik?
Hanya jika berada di balik autentikasi. Store meminta kredensial Jellyfin, tetapi mengaktifkan Remote Play akan memberikan sesi Jellyfin Anda kepada server. Akibatnya, siapa pun yang membuka /remote.html dapat memperoleh instance library Anda yang sebenarnya tanpa login. Tempatkan reverse proxy dengan single sign-on di depannya, atau jangan publikasikan hostname tersebut di DNS publik dan akses store melalui VPN.
Mengapa rak kosong setelah saya login?
Browser memanggil API Jellyfin secara langsung, sehingga Jellyfin harus dapat dijangkau dari browser, bukan hanya dari VPS. Buka konsol browser. blocked by CORS policy berarti Jellyfin tidak menerima permintaan dari alamat Halcyon. Pesan Mixed Content berarti halaman menggunakan HTTPS, sedangkan alamat Jellyfin yang Anda masukkan menggunakan HTTP biasa.
Apakah saya memerlukan --network host?
Hanya untuk Remote Play. WebRTC harus mengiklankan alamat asli mesin. Di balik bridge Docker, container hanya dapat menawarkan alamat 172.x yang tidak dapat dijangkau oleh ponsel mana pun di jaringan Anda. Untuk menjelajahi store di browser, -p 1420:1420 sudah berfungsi dan mengekspos host dalam tingkat yang jauh lebih kecil.
Tag image mana yang harus saya gunakan?
Kunci digest, bukan latest. Baca digest untuk versi yang memiliki docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.3.1, jalankan digest tersebut, dan lakukan pemutakhiran hanya setelah membaca catatan rilis. Per Agustus 2026, image yang dipublikasikan hanya linux/amd64, sehingga host arm64 harus melakukan build dari hasil clone dengan docker compose up -d.