SSD Nodes Learn Hosting plans →
تعلیمی Matt Connorتحریر: Matt Connor

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 پر بنائی گئی ہے۔ یہ JavaScript library ہے جو WebGL (web graphics library، یعنی browser کا GPU سے رابطہ) کے ذریعے 3D graphics بناتی ہے۔ Store geometry اور box art کو وہ machine composited کرتی ہے جس سے screen منسلک ہے۔

Container کا کام بہت محدود ہے۔ یہ npm run serve چلاتا ہے، جو vite preview --port 1420 --strictPort --host ہے، اور built files کے ساتھ چند مختصر middleware routes فراہم کرتا ہے۔ Halcyon کوئی transcoding نہیں کرتی اور server پر کوئی engine نہیں چلاتی۔

اس لیے GPU سے متعلق سوال client کے بارے میں ہے۔ ایک چھوٹا VPS یہ کام آسانی سے انجام دے سکتا ہے، کیونکہ اس میں static files اور HTTP کے ذریعے چند ہلکے routes فراہم کرنے ہوتے ہیں۔ Browser چلانے والا laptop، tablet یا television یہ طے کرتا ہے کہ store روانی سے چلے گا یا بہت سست ہو گا۔

ایک feature اس اصول سے مستثنیٰ ہے۔ Remote Play کے private instances server پر ہر visitor کے لیے ایک headless Chromium شروع کرتے ہیں، پھر rendered store کو WebRTC (web real time communication) کے ذریعے phone یا set top box پر stream کرتے ہیں۔ اس طریقے میں rendering server پر ہوتی ہے۔ Default طور پر زیادہ سے زیادہ دو instances چلتے ہیں، اور یہ حد REMOTE_PLAY_MAX_INSTANCES سے تبدیل کی جا سکتی ہے۔ وہاں GPU بھی درکار ہوتا ہے۔ Published image میں software renderer شامل نہیں ہے، اس لیے mapped /dev/dri render node کے بغیر server private store شروع کرنے سے انکار کر دیتا ہے اور This store server has no GPU available, so it can’t render a private store. جواب دیتا ہے۔

آپ کی 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 کا موازنہ کریں۔

انسٹال کرنے سے پہلے ویڈیو اسٹور کا ڈیمو آزمائیں

پروجیکٹ ایک ڈیمو فراہم کرتا ہے جو کسی server اور login کے بغیر چلتا ہے۔ موجودہ source میں hosted build اپنی shelves کو streaming titles سے بھرتی ہے، نہ کہ آپ کی library جیسی ترتیب سے؛ اس لیے اسے store کا ایک جائزہ سمجھیں۔ اپنی deployment کے URL کے آخر میں ?demo=1 شامل کرنے سے اس کے بجائے ایک synthetic library لوڈ ہوتی ہے۔

یہ synthetic library hardware test ہے، لیکن ایک اہم شرط کے ساتھ: یہ خود کو device کے مطابق سائز کرتی ہے۔ اضافی GPU گنجائش رکھنے والا browser تقریباً 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

اس کے بعد تصدیق کریں کہ یہ شروع ہو گیا ہے۔

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

Log میں یہ دکھائی دینا چاہیے کہ preview server port 1420 پر listening کر رہا ہے، اور curl کو HTTP/1.1 200 OK کا جواب دینا چاہیے۔ جو container چند seconds کے اندر exit ہو جائے، اس کی وجہ تقریباً ہمیشہ port ہوتا ہے۔ --strictPort کا مطلب ہے کہ 1420 مصروف ہونے پر server خود کو 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-video

VPS پر یہ بہتر default ہے، کیونکہ host networking container کو machine کے ہر interface پر رکھتی ہے، جس میں public interface بھی شامل ہے۔ اس trade-off کی باقی تفصیل VPS پر Docker چلانا میں ہے۔ --restart unless-stopped reboot کے بعد store کو دوبارہ شروع کرتا ہے۔ یہ وہی تصور ہے جو boot پر شروع ہونے والی Compose services میں استعمال ہوتا ہے۔

Repository کو clone کر کے docker compose up -d چلانے سے image مقامی طور پر build ہوتی ہے۔ Committed Compose file default طور پر source سے build کرتی ہے اور پہلے سے built image: line کو comment out رکھتی ہے۔ اگر آپ Compose کے تحت published image استعمال کرنا چاہتے ہیں تو اس line کا comment ہٹا دیں۔

Published image linux/amd64 اور linux/arm64 دونوں کو support کرتی ہے۔ 11 October 2026 کو کی گئی جانچ کے مطابق، موجودہ 0.25.0 release ہر platform کے لیے الگ digest درج کرتی ہے، اس لیے arm64 VPS اسی command سے image pull کرتا ہے جس سے کوئی دوسرا host کرتا ہے۔ صرف amd64 کی پابندی مختصر مدت تک رہی: changelog نے 8 August 2026 کو v0.2.1 میں arm64 image ہٹا دی، اور 11 August 2026 کو v0.4.0 میں اسے native runners پر build کر کے دوبارہ شامل کر دیا۔ صرف وہ host جسے اس مدت کے کسی tag پر pin کیا گیا ہو، 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 کو بالکل draw نہ کر سکے، اس کے لیے اس کا کوئی فائدہ نہیں۔

ایسے clients کے لیے 2.5D mode موجود ہے۔ یہ سادہ HTML اور CSS استعمال کرتا ہے اور WebGL کے بغیر چلتا ہے۔ اسے Raspberry Pi جتنے کم وسائل والے hardware کے لیے بنایا گیا ہے۔ آپ page reload کیے بغیر settings یا power menu سے 3D اور 2.5D کے درمیان تبدیل کر سکتے ہیں۔ اس لیے ایک ہی device پر دونوں modes آزمانے میں چند seconds لگتے ہیں۔ توقعات حقیقت پسندانہ رکھیں: August 2026 میں author نے flat mode کو rough قرار دیا اور کہا کہ project ابھی بمشکل شروع ہوا تھا۔ اسے کمزور clients کے لیے fallback سمجھیں۔

جب کوئی client 3D store چلانے کے لیے بہت کم وسائل رکھتا ہو تو failure واضح ہوتا ہے۔ tab خود reload ہو جاتا ہے، یا browser lost WebGL context کی اطلاع دیتا ہے۔ یہ عموماً اس وقت ہوتا ہے جب shelves ابھی بھر رہی ہوں۔ اپنی library کم کرنے کے بجائے اس device کو 2.5D پر منتقل کریں۔

image کو pin کریں، اور pull کرنے سے پہلے جانچیں

اس حصے کو سنجیدگی سے لیں۔ v0.1.0 سے v0.3.1 تک کے tags ایک دوسرے کے چند دن کے اندر جاری ہوئے، اور v0.2.1 صرف اس لیے موجود ہے کہ v0.2.0 کے لیے image push ناکام ہو گئی تھی۔ اس کے بعد سے رفتار کم نہیں ہوئی: v0.25.0 کو 7 October 2026 کو، v0.23.0 کے صرف تین دن بعد، publish کیا گیا۔ upstream پر bug reports خوش آئند ہیں، لیکن patches قبول نہیں کیے جاتے، اس لیے release stream ایک شخص کی موجودہ working state ہے۔

docker pull کی عادت کے ساتھ latest چلانے کا مطلب یہ ہے کہ کسی بھی عام Tuesday کو store آپ کے لیے بدل سکتا ہے۔ digest کے ذریعے pin کریں؛ یہی واحد reference ہے جو تبدیل نہیں ہو سکتا۔

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

یہ command tag کے پیچھے موجود digest دکھاتی ہے۔ اسے tag کی جگہ استعمال کریں۔

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

11 October 2026 کو یہی 0.25.0 tag کے پیچھے موجود digest تھا۔ اسے copy کرنے کے بجائے موجودہ digest خود پڑھیں، اور تبدیلی کرنے سے پہلے release notes پڑھیں، کیونکہ یہاں patch release میں fixes کے ساتھ store layout میں تبدیلیاں بھی شامل ہو سکتی ہیں۔

FAQ

کیا Halcyon کو میرے VPS پر GPU درکار ہے؟

عام استعمال کے لیے نہیں۔ Store کو browser میں three.js بناتا ہے، اس لیے rendering client machine پر ہوتی ہے، جبکہ container built files اور port 1420 پر چند ہلکے server routes فراہم کرتا ہے۔ استثنا Remote Play کی private instances ہیں، جو server پر headless Chromium چلاتی اور نتیجہ stream کرتی ہیں۔ شائع شدہ image کے ساتھ اس راستے کے لیے /dev/dri کو container میں map کرنا ضروری ہے۔ GPU کے بغیر server private store شروع کرنے سے انکار کر دیتا ہے۔

کیا میں Halcyon کو public internet پر رکھ سکتا ہوں؟

صرف authentication کے پیچھے۔ Store Jellyfin credentials طلب کرتا ہے، لیکن Remote Play فعال کرنے سے آپ کا Jellyfin session server کو مل جاتا ہے۔ اس لیے /remote.html load کرنے والا ہر شخص login کیے بغیر آپ کی اصل library کی instance حاصل کر لیتا ہے۔ اس کے سامنے single sign on والا reverse proxy رکھیں، یا hostname کو public DNS سے خارج رکھیں اور VPN کے ذریعے store تک رسائی حاصل کریں۔

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 میں store browse کرنے کے لیے -p 1420:1420 کافی ہے اور host کی بہت کم معلومات ظاہر کرتا ہے۔

مجھے کون سا image tag استعمال کرنا چاہیے؟

latest کے بجائے digest کو pin کریں۔ docker buildx imagetools inspect ghcr.io/halcyon-video/halcyon-video:0.25.0 والی version کا digest پڑھیں، وہ digest چلائیں، اور release notes پڑھنے کے بعد ہی اسے تبدیل کریں۔ 11 October 2026 تک شائع شدہ image میں linux/amd64 اور linux/arm64 دونوں شامل ہیں، اس لیے arm64 host اسے کسی بھی دوسری image کی طرح pull کر لیتا ہے اور clone سے build کرنے کی ضرورت نہیں رہتی۔