Halcyon سے Jellyfin کو 1990s کی ویڈیو اسٹور بنائیں
Halcyon آپ کی Jellyfin لائبریری کو browser میں چلنے پھرنے والی 1990s کی rental store بناتا ہے۔ Docker command، reverse proxy اور اس کی حقیقی حدود جانیں۔
Halcyon آپ کی Jellyfin لائبریری کے ساتھ کیا کرتا ہے
Halcyon Video آپ کی Jellyfin لائبریری کو browser میں چلنے پھرنے کے قابل 1990s کی ویڈیو اسٹور میں تبدیل کر دیتا ہے۔ آپ کی ہر فلم shelf پر ایک case بن جاتی ہے۔ آپ strip lights کے نیچے aisles میں چلتے ہیں، ایک box نیچے اتارتے ہیں، اس کی پشت پر موجود specifications پڑھنے کے لیے اسے پلٹتے ہیں، اور playback شروع کرنے کے لیے اسے counter تک لے جاتے ہیں۔ Playback کے آغاز، پیش رفت اور رکنے کی معلومات Jellyfin کو واپس بھیجی جاتی ہیں، اس لیے resume points اور watch history درست رہتی ہے۔
Halcyon موجودہ Jellyfin server کو Jellyfin API کے ذریعے پڑھتا ہے اور اپنی کوئی library محفوظ نہیں رکھتا۔ یہ guide فرض کرتی ہے کہ Jellyfin پہلے سے چل رہا ہے اور library کو درست طور پر scan کر رہا ہے۔ اگر ایسا نہیں ہے تو پہلے VPS پر Jellyfin کو media server کے طور پر ترتیب دیں، پھر اس وقت واپس آئیں جب normal web client میں آپ کی library درست نظر آنے لگے۔ اسے اس لیے install کیا جاتا ہے کہ library پہلے سے موجود ہے، نہ کہ اس لیے کہ آپ کو اپنی self hosting list میں ایک اور service درکار تھی۔
یہ project GPL-3.0 کے تحت جاری کیا گیا ہے اور اسے ایک شخص نے لکھا ہے۔ README میں واضح طور پر درج ہے کہ یہ pull requests قبول نہیں کرتا۔ Development تیزی سے ہوتی ہے اور regression پکڑنے کے لیے کوئی دوسرا maintainer موجود نہیں، اس لیے store کسی اور کو دکھانے سے پہلے image version کو pin کر دیں۔ آخری section میں اس کا طریقہ بتایا گیا ہے۔
رینڈرنگ کہاں ہوتی ہے؟
براؤزر میں۔ Halcyon، Vite اور TypeScript ایپ ہے جو three.js پر مبنی ہے۔ three.js ایک JavaScript لائبریری ہے جو WebGL (web graphics library، یعنی براؤزر کا GPU کے ساتھ interface) کے ذریعے 3D گرافکس بناتی ہے۔ Store کی geometry اور box art اس مشین پر composited ہوتے ہیں جس سے screen منسلک ہوتی ہے۔
Container بہت کم کام کرتا ہے۔ یہ npm run serve چلاتا ہے، جو vite preview --port 1420 --strictPort --host ہے، اور built files کے ساتھ چند چھوٹے middleware routes serve کرتا ہے۔ Halcyon کوئی transcoding شامل نہیں کرتا اور server پر کوئی engine نہیں چلاتا۔
اس لیے GPU سے متعلق سوال client سے وابستہ ہے۔ ایک چھوٹا VPS یہ کام باآسانی انجام دے سکتا ہے، کیونکہ اس کا مطلب HTTP کے ذریعے static files serve کرنا ہے۔ Browser چلانے والا laptop، tablet یا television یہ طے کرتا ہے کہ store روانی سے چلے گا یا آہستہ ہو جائے گا۔
ایک feature اس اصول سے مستثنیٰ ہے۔ Remote Play server پر headless Chromium instances شروع کرتا ہے اور rendered store کو WebRTC (web real time communication) کے ذریعے phone یا set top box پر stream کرتا ہے۔ اس راستے میں rendering server پر ہوتی ہے۔ Default طور پر زیادہ سے زیادہ 2 instances چلتے ہیں، اور اس حد کو REMOTE_PLAY_MAX_INSTANCES کے ذریعے تبدیل کیا جا سکتا ہے۔ کسی mapped /dev/dri device کے بغیر یہ instances CPU پر render کرتے ہیں، اس لیے 2 core VPS ہر اضافی viewer کا اثر محسوس کرتا ہے۔
آپ کی library سے store کیا پڑھتا ہے
Aisles، Jellyfin کی اپنی structure سے آتی ہیں۔ Halcyon آپ کی libraries اور genres کی بنیاد پر sections ترتیب دیتا ہے، اور آپ کے BoxSets سے sequels کو group کرتا ہے۔ ہر case کی پشت پر چھپی specs، MediaStreams metadata سے آتی ہیں جسے Jellyfin پہلے ہی محفوظ رکھتا ہے۔ اس کا مطلب ہے کہ Jellyfin میں جو چیز missing ہو، وہ shelf پر بھی missing ہوگی۔
اس طرح store آپ کے metadata کا درست آئینہ بن جاتا ہے۔ Docker Compose میں arr stack سے فراہم کی گئی library، جس میں artwork اور genres پہلے ہی مکمل ہوں، یہاں generic names والی الگ الگ files کے folder سے کہیں بہتر دکھائی دیتی ہے۔ Photo libraries کا انحصار بھی اسی چیز پر ہوتا ہے جس نے انہیں index کیا ہو۔ اس نکتے کو یاد رکھیں جب آپ اسی server پر موجود stills کے لیے PhotoPrism اور Immich کا موازنہ کریں۔
انسٹال کرنے سے پہلے video store demo آزمائیں
یہ project ایک synthetic library کے ساتھ مکمل store چلاتا ہے، جسے hosted demo پر آزمایا جا سکتا ہے۔ اپنے deployment میں کسی بھی Halcyon URL کے آخر میں ?demo=1 شامل کرنے سے یہی نتیجہ حاصل ہوتا ہے۔
اسے hardware test کے طور پر استعمال کریں، لیکن ایک اہم بات ذہن میں رکھیں: demo خود کو device کے مطابق محدود کرتا ہے۔ جس browser کے پاس GPU کی اضافی گنجائش ہو، اسے تقریباً 2,000 titles پر مشتمل مکمل store ملتا ہے۔ درمیانے درجے کے GPU کو تقریباً 800 titles ملتے ہیں۔ phone یا ایسا browser جو device memory کو 4 GB یا اس سے کم رپورٹ کرے، اسے تقریباً 300 titles ملتے ہیں۔ کمزور device پر ہموار چلنے والا demo پہلے ہی خود کو محدود کر چکا ہوتا ہے، اس لیے اس سے یہ اندازہ کم ہی ہوتا ہے کہ آپ کی مکمل library وہاں کیسے چلے گی۔ مفید test وہ device ہے جسے مکمل store ملتا ہے۔ اگر اس میں stutter ہو، تو اسی حجم کی آپ کی اپنی library میں بھی stutter ہوگا۔ اس کا حل بڑا VPS نہیں، بلکہ ذیل میں بیان کردہ 2.5D mode ہے۔
اسے Docker کے ساتھ چلائیں
یہ وہ command ہے جسے upstream documentation میں درج کیا گیا ہے۔
docker run -d --name halcyon --network host --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoپھر تصدیق کریں کہ یہ start ہو گیا ہے۔
docker logs halcyon
curl -I http://127.0.0.1:1420log میں preview server کو port 1420 پر listening دکھائی دینا چاہیے، اور curl کو HTTP/1.1 200 OK کا جواب دینا چاہیے۔ جو container چند seconds میں exit ہو جائے، اس کی وجہ تقریباً ہمیشہ port ہوتی ہے۔ --strictPort کا مطلب ہے کہ server، 1420 کے مصروف ہونے پر 1421 پر منتقل ہونے سے انکار کرتا ہے، اس لیے رک جاتا ہے۔
--network host Remote Play کے لیے ہے، store کے لیے نہیں۔ WebRTC کو stream طلب کرنے والے device کے لیے machine کا حقیقی address advertise کرنا ہوتا ہے۔ Default Docker bridge کے پیچھے container صرف اپنا 172.x address جانتا ہے، جس تک آپ کے network کا کوئی phone نہیں پہنچ سکتا، اس لیے stream کبھی connect نہیں ہوتی۔ اگر آپ صرف browser میں store استعمال کرنا چاہتے ہیں تو port publish کریں۔
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-videoVPS پر یہ بہتر default ہے، کیونکہ host networking container کو machine کے ہر interface پر رکھتی ہے، جس میں public interface بھی شامل ہے۔ VPS پر Docker چلانا اس trade-off کی باقی تفصیل بیان کرتا ہے۔ --restart unless-stopped reboot کے بعد store کو دوبارہ start کرتا ہے؛ یہ وہی تصور ہے جو boot پر start ہونے والی Compose services میں استعمال ہوتا ہے۔
Repository clone کرنے اور docker compose up -d چلانے سے image مقامی طور پر build ہوتی ہے۔ committed Compose file default طور پر source سے build کرتی ہے اور پہلے سے build شدہ image: line کو comment میں رکھتی ہے، اس لیے اگر آپ Compose کے تحت published image استعمال کرنا چاہتے ہیں تو اس line کا comment ہٹا دیں۔
Published image، linux/amd64 اور linux/arm64 دونوں کو support کرتی ہے۔ 9 October 2026 کو کی گئی جانچ کے مطابق، موجودہ 0.25.0 release ہر platform کے لیے الگ digest درج کرتی ہے، اس لیے arm64 VPS اسی command سے image pull کرتا ہے جس سے کوئی دوسرا host کرتا ہے۔ amd64-only مرحلہ مختصر تھا: changelog نے 8 August 2026 کو v0.2.1 میں arm64 image ہٹا دی اور 11 August 2026 کو v0.4.0 میں native runners پر build شدہ image دوبارہ شامل کر دی۔ صرف وہ host جو اس مدت کے کسی tag پر pinned ہو، اسے clone سے build کرنا ہوگا۔
Jellyfin سرور سے اسے مربوط کریں
http://<host>:1420 کھولیں اور اپنے Jellyfin سرور کا address، username اور password استعمال کرکے لاگ ان کریں۔ repository میں موجود .env.local.example فائل صرف local development کے لیے ہے۔ Vite، VITE_ سے شروع ہونے والے variables کو client-side code کے لیے ظاہر کرتا ہے، اس لیے وہاں لکھا گیا Jellyfin password ہر visitor کے download کیے جانے والے JavaScript bundle میں شامل ہو جاتا ہے۔ ایسے server پر، جس تک دوسرے لوگ بھی رسائی حاصل کر سکتے ہوں، interface کے ذریعے لاگ ان کریں۔
Browser براہ راست Jellyfin سے رابطہ کرتا ہے۔ Halcyon کا container، Jellyfin API کے لیے proxy کا کام نہیں کرتا۔ Debugging شروع کرنے سے پہلے اس کے دو اہم نتائج سمجھ لیں۔
اول، Jellyfin کو صرف Halcyon فراہم کرنے والے VPS سے نہیں، بلکہ browser سے بھی قابل رسائی ہونا چاہیے۔ 127.0.0.1:8096 سے bind کیا گیا Jellyfin local test کے لیے درست ہے، لیکن باقی تمام users کے لیے shelves خالی رہیں گی۔
دوم، یہ call cross-origin ہوتی ہے، یعنی Halcyon کے address سے Jellyfin کے address تک جاتی ہے۔ Jellyfin بطور default API requests کا جواب Access-Control-Allow-Origin: * کے ساتھ دیتا ہے، اس لیے اضافی configuration کے بغیر کام ہو جاتا ہے۔ اگر آپ نے یہ setting محدود کر دی ہو، یا Jellyfin API کے سامنے authentication proxy لگا دی ہو، تو browser console blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource دکھاتا ہے اور store میں خالی shelves نظر آتی ہیں۔
اسے reverse proxy کے پیچھے رکھیں اور سامنے authentication لگائیں
vite preview ایک preview server ہے۔ یہ TLS (transport layer security) کو terminate نہیں کرتا اور اس میں اپنا access control بھی نہیں ہے۔ اس لیے public استعمال کی صورت میں اسے nginx یا 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;
}
}Container کے سامنے domain name استعمال کرنے کے لیے ایک اور setting درکار ہے۔ Halcyon DNS rebinding سے تحفظ کے لیے localhost، raw IP addresses اور اس machine کے ناموں پر جواب دیتا ہے جہاں یہ چل رہا ہو۔ Container کے اندر وہ machine خود container ہوتی ہے، اس لیے اس کا hostname آپ کا hostname نہیں ہوتا۔ halcyon.example.com کے طور پر آنے والی request مسترد ہو جاتی ہے، اور response میں مسترد کیے گئے host کا نام درج ہوتا ہے۔ وہ نام شامل کریں۔
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یہ value comma سے الگ کی جاتی ہے۔ .example.com جیسا ابتدائی dot subdomains سے بھی match کرتا ہے، جبکہ all اس check کو بند کر دیتا ہے۔ all صرف ایسی machine پر استعمال کریں جس تک باہر سے کوئی رسائی نہ ہو۔
جب store کو https:// کے ذریعے serve کیا جائے تو login کے وقت درج کیا جانے والا Jellyfin address بھی https:// ہونا چاہیے۔ Browser، HTTPS page سے کی جانے والی plain http:// API call کو block کر دیتا ہے، جبکہ console Mixed Content: The page at 'https://halcyon.example.com/' was loaded over HTTPS, but requested an insecure resource پڑھتا ہے۔ Login خاموشی سے fail ہو جاتا ہے اور Halcyon میں اس کی کوئی وضاحت نہیں ملتی۔ دونوں کو TLS کے ذریعے serve کریں، یا private network کے اندر دونوں کے لیے plain HTTP استعمال کریں۔
اب authentication کی باری ہے۔ Store، Jellyfin credentials مانگتا ہے، اس لیے URL تلاش کرنے والے اجنبی کو login screen دکھائی دیتی ہے۔ ایک feature اس صورت حال کو بدل دیتا ہے۔ Settings اور پھر Connection کے تحت Remote Play کو فعال کرنے سے آپ کا Jellyfin session server کو مل جاتا ہے، اور /remote.html پر آنے والے visitors کو آپ کی اصل library کا اپنا instance مل جاتا ہے۔ یہی اس feature کا مقصد ہے، اور اس کا مطلب ہے کہ URL کی رازداری ہی internet اور آپ کی films کے درمیان واحد رکاوٹ رہ جاتی ہے۔ اگر Remote Play فعال کریں تو پورے site کے سامنے single sign on لگائیں، جس کے لیے Authentik بطور self hosted SSO gateway استعمال کیا جا سکتا ہے، یا public hostname ہٹا کر store تک wg-easy کے زیر انتظام WireGuard tunnel کے ذریعے رسائی حاصل کریں۔
اس کے ساتھ دو باتیں اہم ہیں۔ reverse proxy صرف store کو forward کرتا ہے۔ Remote Play stream UDP پر WebRTC ہے اور HTTP proxy سے نہیں گزرتی، اس لیے اسے 3478/udp پر اپنی الگ path درکار ہوتی ہے، اور bundled TURN relay استعمال ہونے کی صورت میں 49200 سے 49260/udp تک بھی۔ دوسرا، اوپر دیا گیا plain docker run کوئی volume محفوظ نہیں رکھتا، اس لیے Remote Play seed docker rm کے بعد باقی نہیں رہتا۔ Compose file اسی وجہ سے halcyon-data volume کو /data پر mount کرتی ہے اور REMOTE_PLAY_SEED کو /data/remote-play-seed.json پر set کرتی ہے۔
جب store خراب کار کرے تو کیا کریں
Halcyon ضرورت کے وقت render ہوتا ہے۔ غیر فعال store کوئی frame composite نہیں کرتا، اور window focus ختم ہونے پر animation loop رک جاتا ہے۔ اسی لیے کھلا چھوڑا گیا tab laptop کی battery کو ختم نہیں کرتا۔ اس سے ایسے machine میں مدد ملتی ہے جو صرف بمشکل مطلوبہ کارکردگی دے رہی ہو۔ لیکن ایسے machine کے لیے یہ کوئی حل نہیں جو store کو بالکل render نہ کر سکے۔
ایسے clients کے لیے 2.5D mode موجود ہے۔ یہ سادہ HTML اور CSS استعمال کرتا ہے اور WebGL کے بغیر چلتا ہے۔ اسے Raspberry Pi جتنے محدود hardware کے لیے بنایا گیا ہے۔ آپ page reload کیے بغیر settings یا power menu سے 3D اور 2.5D کے درمیان تبدیل کر سکتے ہیں۔ اس لیے ایک ہی device پر دونوں modes آزمانے میں صرف چند seconds لگتے ہیں۔ توقعات حقیقت پسندانہ رکھیں۔ author کے مطابق flat mode ابھی rough ہے اور اس پر کام جاری ہے۔ اسے کمزور clients کے لیے fallback سمجھیں۔
جب کوئی client 3D store چلانے کے لیے بہت کمزور ہو تو failure واضح طور پر ظاہر ہوتا ہے۔ tab خود reload ہو جاتا ہے، یا browser lost WebGL context کی اطلاع دیتا ہے۔ یہ عموماً اس وقت ہوتا ہے جب shelves ابھی بھر رہی ہوں۔ اپنی library کم کرنے کے بجائے اس device کو 2.5D پر منتقل کریں۔
image کو pin کریں، اور pull کرنے سے پہلے جانچ لیں
اس حصے کو سنجیدگی سے لیں۔ Tags v0.1.0 سے v0.3.1 تک چند ہی دنوں کے اندر جاری ہوئے، اور v0.2.1 صرف اس لیے موجود ہے کہ v0.2.0 کے لیے image push ناکام ہو گیا تھا۔ اس کے بعد بھی رفتار کم نہیں ہوئی: v0.25.0 کو 7 October 2026 کو شائع کیا گیا، جو v0.23.0 کے صرف 3 دن بعد تھا۔ Upstream میں bug reports خوش آمدید ہیں، لیکن patches نہیں؛ اس لیے release stream ایک شخص کی موجودہ working state ہے۔
latest کو docker pull کی عادت کے ساتھ چلانے کا مطلب ہے کہ کسی بھی عام Tuesday کو store آپ کے نیچے تبدیل ہو سکتا ہے۔ Digest کے ذریعے pin کریں، کیونکہ یہی واحد reference ہے جو تبدیل نہیں ہو سکتا۔
docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.25.0یہ tag کے پیچھے موجود digest دکھاتا ہے۔ اسے tag کی جگہ استعمال کریں۔
docker run -d --name halcyon -p 1420:1420 --restart unless-stopped \
ghcr.io/halcyon-video/halcyon-video@sha256:7fc00e619a30004d275faa79ad36d810e5a8530757132603065ba4e848dd67de9 October 2026 کو وہ digest 0.25.0 تھا۔ اسے نقل کرنے کے بجائے موجودہ digest خود پڑھیں، اور تبدیلی سے پہلے release notes بھی پڑھیں، کیونکہ یہاں patch release میں fixes کے ساتھ store layout کی تبدیلیاں بھی شامل ہو سکتی ہیں۔
FAQ
کیا میرے VPS پر Halcyon کے لیے GPU درکار ہے؟
عام استعمال کے لیے نہیں۔ اسٹور کو browser میں three.js رینڈر کرتا ہے، اس لیے rendering client machine پر ہوتی ہے اور container صرف port 1420 پر static files فراہم کرتا ہے۔ استثنا Remote Play ہے، جو server پر headless Chromium چلاتا ہے اور نتیجہ stream کرتا ہے۔ یہ طریقہ CPU پر rendering کرتا ہے، جب تک کہ hardware acceleration کے لیے آپ /dev/dri کو container میں map نہ کریں۔
کیا میں Halcyon کو public internet پر رکھ سکتا ہوں؟
صرف authentication کے پیچھے۔ اسٹور Jellyfin credentials طلب کرتا ہے، لیکن Remote Play فعال کرنے سے آپ کا Jellyfin session server کو فراہم ہو جاتا ہے۔ اس لیے /remote.html لوڈ کرنے والا ہر شخص login کیے بغیر آپ کی اصل library کی ایک instance حاصل کر سکتا ہے۔ اس کے سامنے single sign on والا reverse proxy رکھیں، یا hostname کو public DNS سے باہر رکھیں اور VPN کے ذریعے اسٹور تک رسائی حاصل کریں۔
login کرنے کے بعد shelves خالی کیوں ہیں؟
browser براہ راست Jellyfin API کو call کرتا ہے، اس لیے Jellyfin کا browser سے reachable ہونا ضروری ہے، صرف VPS سے نہیں۔ browser console کھولیں۔ blocked by CORS policy کا مطلب ہے کہ Jellyfin، Halcyon کے address سے آنے والی request قبول نہیں کر رہا۔ Mixed Content message کا مطلب ہے کہ page HTTPS پر ہے، جبکہ آپ نے جو Jellyfin address درج کیا ہے وہ plain HTTP ہے۔
کیا مجھے --network host درکار ہے؟
صرف Remote Play کے لیے۔ WebRTC کو machine کا حقیقی address advertise کرنا ہوتا ہے، اور Docker bridge کے پیچھے container صرف 172.x address پیش کر سکتا ہے، جس تک آپ کے network کا کوئی phone نہیں پہنچ سکتا۔ browser میں اسٹور براؤز کرنے کے لیے -p 1420:1420 کافی ہے اور host کا بہت کم حصہ expose کرتا ہے۔
مجھے کون سا image tag استعمال کرنا چاہیے؟
latest کے بجائے digest کو pin کریں۔ docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.25.0 والی version کا digest پڑھیں، اسی digest کو چلائیں، اور release notes پڑھنے کے بعد ہی اسے تبدیل کریں۔ 9 October 2026 تک published image میں linux/amd64 اور linux/arm64 دونوں شامل ہیں، اس لیے arm64 host اسے کسی بھی دوسری image کی طرح pull کرتا ہے اور clone سے build کرنے کی ضرورت نہیں ہوتی۔