यह पृष्ठ अंग्रेज़ी से अनुवादित है। अंग्रेज़ी पृष्ठ पढ़ें
Zeyn मीडिया फ़ाइलें HTTP range अनुरोधों के साथ परोसता है और उन्हें Video और Audio कंपोनेंट में चलाता है। नेटिव <video> और <audio> एलिमेंट डिकोडिंग करते हैं; Zeyn उनके ऊपर अपने सुगम्य कंट्रोल बनाता है।
फ़ाइलें परोसना
feature पर अनुमति के साथ MediaSource पंजीकृत करें:
Feature::new("library")
.media("films", "library.watch", FilmFolder::new())
.media("music", "library.listen", SongTable::new())ब्राउज़र GET /_zeyn/media/{source}/{id} लाता है। स्रोत कुछ भी देखे उससे पहले सर्वर:
- ऐसी आईडी अस्वीकार करता है जो
A-Z a-z 0-9 - _ .के 1 से 128 वर्ण नहीं हैं, बिंदु से शुरू होती हैं या..रखती हैं (404); - स्रोत की अनुमति (
403) और feature के गार्ड जाँचता है; - इंटरसेप्टर चलाता है (
CallKind::Media,screenमें स्रोत के नाम के साथ)।
open(ctx, id) आईडी को ऐप की अपनी कैटलॉग में खोजता है और Media लौटाता है: डिस्क की फ़ाइल के लिए Media::file(path) (प्रकार एक्सटेंशन से, entity टैग आकार और संशोधन समय से), मेमोरी के डेटा के लिए Media::bytes(data, type), या किसी भी Read + Seek के लिए Media::new(reader, len, type)। आईडी एक नाम है, कभी पथ नहीं: पथ अपनी खोज से बनाएँ, आईडी से नहीं।
उत्तर:
| अनुरोध | उत्तर |
|---|---|
कोई Range नहीं, दूसरी इकाई, या कई रेंज | पूरी फ़ाइल के साथ 200 |
bytes=a-b, bytes=a-, bytes=-n | Content-Range: bytes a-b/len के साथ 206 |
| बाइट रेंज जो गलत रूप की हो या अंत के पार से शुरू हो | Content-Range: bytes */len के साथ 416 |
If-Range जो entity टैग से मेल न खाए | पूरी फ़ाइल के साथ 200 |
If-None-Match जो entity टैग से मेल खाए | 304 |
हर उत्तर में Accept-Ranges: bytes, Cache-Control: private, no-cache और X-Content-Type-Options: nosniff होता है। केवल ऑडियो, वीडियो, रास्टर छवियाँ, WebVTT और स्ट्रीमिंग प्लेलिस्ट इनलाइन परोसी जाती हैं; कोई भी अन्य प्रकार (HTML, SVG, स्क्रिप्ट) application/octet-stream के रूप में Content-Disposition: attachment के साथ भेजा जाता है, इसलिए फ़ाइल कभी ऐप के पृष्ठ के रूप में नहीं चल सकती। फ़ाइलें blocking पूल पर 64 KiB के टुकड़ों में पढ़ी जाती हैं, अधिकतम चार टुकड़े कतार में, इसलिए किसी भी फ़ाइल आकार पर मेमोरी सपाट रहती है और जो क्लाइंट चला जाए वह पढ़ना रोक देता है। दर सीमाएँ अन्य अनुरोधों की तरह लागू होती हैं; मीडिया पठन लेखन नहीं हैं।
content security policy में बदलाव की ज़रूरत नहीं: default-src 'self' पहले से ऐप से मीडिया की अनुमति देता है। जो एडैप्टिव स्ट्रीमिंग लाइब्रेरी Media Source Extensions इस्तेमाल करती है उसे media-src में blob: चाहिए, जिसे Zeyn नहीं जोड़ता।
सार्वजनिक फ़ाइलें: छवियाँ, लोगो, डाउनलोड
जो फ़ाइलें कोई भी देख सकता है, साइन इन हो या नहीं, वे सार्वजनिक स्रोत में जाती हैं:
Feature::new("catalog").public_media("images", 86_400, ImageFolder::new("data/images"))GET /_zeyn/media/images/{id} अनुमति जाँच छोड़ता है (आईडी, गार्ड और इंटरसेप्टर फिर भी चलते हैं), कोई कुकी सेट नहीं करता और Cache-Control: public, max-age=86400 के साथ उत्तर देता है (अधिकतम MAX_PUBLIC_MEDIA_CACHE, एक वर्ष), इसलिए ब्राउज़र और ऐप के सामने का CDN उसे रखते हैं। entity टैग उन्हें अब भी 304 से पुनः सत्यापित करने देता है। पथ को Image के src के रूप में इस्तेमाल करें। कई बड़ी छवियों के लिए CDN के पीछे अलग छवि होस्ट अब भी सस्ता विकल्प है; उसे App::csp(|c| c.img_src([...])) से अनुमति दें।
openको हर आगंतुक के लिए वही बाइट लौटाने होंगे। सार्वजनिक फ़ाइल को कभी उपयोगकर्ता, भूमिका, टेनेंट या भाषा से न बदलें: CDN या प्रॉक्सी पहला उत्तर रख लेता है और उसे सबको देता है। जो फ़ाइलें प्रति उपयोगकर्ता अलग हों वेFeature::mediaसे पंजीकृत स्रोत में जाती हैं।- लंबे
max_ageके साथ ऐसी आईडी दें जो सामग्री के साथ बदलें, जैसे सामग्री हैश (poster-3f9a2c.jpg)। तब बदली फ़ाइल को नया पता मिलता है, पुरानी प्रतियाँ समाप्त होने तक कैश में नहीं रहतीं। - सार्वजनिक स्रोतों के पठन (
GETऔरHEAD)App::rate_limitमें नहीं गिने जाते: एक पृष्ठ दर्जनों पोस्टर लोड कर सकता है, और कई मोबाइल आगंतुक कैरियर NAT के पीछे एक पता साझा करते हैं। निजी स्रोत अब भी गिने जाते हैं, और IP अनुमति तथा अस्वीकार नियम दोनों पर लागू रहते हैं। बैंडविड्थ दुरुपयोग सीमित करना ऐप के सामने के CDN या रिवर्स प्रॉक्सी का काम है।
प्लेयर
Video::new("films", t!("films.harbor"))
.subtitle(t!("films.harbor_sub"))
.file("harbor.webm")
.file("harbor.mp4")
.hls("harbor.m3u8")
.poster("harbor.jpg")
.captions("harbor.en.vtt", "en", "English")
.captions("harbor.tr.vtt", "tr", "Türkçe")
Audio::new("music", t!("songs.dawn")).subtitle(t!("songs.artist")).file("dawn.mp3")कंट्रोल: चलाना और रोकना, सीक स्लाइडर, समय, म्यूट, आवाज़ स्लाइडर, प्लेबैक गति मेनू, कैप्शन बटन और (एक से अधिक कैप्शन फ़ाइल होने पर) भाषा मेनू, और वीडियो के लिए पिक्चर इन पिक्चर (जहाँ ब्राउज़र समर्थन करे) और पूर्ण स्क्रीन। स्लाइडर WAI-ARIA slider पैटर्न का पालन करते हैं: तीर 5 सेकंड (या आवाज़ का 5 प्रतिशत) हिलाते हैं, Page Up और Page Down दसवाँ हिस्सा हिलाते हैं, Home और End सिरों पर जाते हैं। मेनू menu button पैटर्न का पालन करते हैं। जब फ़ोकस प्लेयर के भीतर हो, k या Space चलाता और रोकता है, j और l 10 सेकंड कूदते हैं, m म्यूट करता है, c कैप्शन टॉगल करता है और f पूर्ण स्क्रीन टॉगल करता है; पृष्ठ पर अन्यत्र ये कुंजियाँ कुछ नहीं करतीं।
कैप्शन WebVTT फ़ाइलें हैं जिन्हें वही स्रोत परोसता है। प्लेयर सक्रिय cue को सादे टेक्स्ट के रूप में बनाता है (cue मार्कअप हटा दिया जाता है), वीडियो के लिए चित्र के ऊपर और ऑडियो के लिए शीर्षक के नीचे, इसलिए कैप्शन हर ब्राउज़र और पूर्ण स्क्रीन में एक जैसे दिखते हैं।
फिर से शुरू करना
प्लेयर सहेजता है कि उपयोगकर्ता कहाँ रुका: चलते समय हर 15 सेकंड, रोकने पर, पृष्ठ छिपने या बंद होने पर, और मीडिया खत्म होने पर 0। जब सहेजा बिंदु किसी भी सिरे से 5 सेकंड से अधिक दूर हो तब वह फिर शुरू करता है।
- जब स्रोत
positionऔरsave_positionलागू करे, स्थितियाँ सर्वर पर जाती हैं:GET /_zeyn/media/position?source=&id={"at": seconds, "kept": true}का उत्तर देता है, और{source, id, at}के साथPOST /_zeyn/media/positionसहेजता है (204)। दोनों आईडी, अनुमति और गार्ड जाँचते हैं और साइन इन उपयोगकर्ता चाहिए; POST को CSRF हेडर भी चाहिए और दर सीमाओं के लिए लेखन गिना जाता है।MAX_POSITION_SECONDSसे ऊपर या शून्य से नीचे की स्थितियाँ422हैं। - वरना (स्रोत की डिफ़ॉल्ट मेथड, या अनाम उपयोगकर्ता) पठन
{"at": null, "kept": false}का उत्तर देता है और प्लेयर स्थिति को ब्राउज़र के local storage मेंzeyn.media.{source}/{id}के नीचे रखता है। स्टोरेज त्रुटियाँ (प्राइवेट विंडो, अवरुद्ध स्टोरेज) अनदेखी की जाती हैं।
साइन इन उपयोगकर्ताओं के लिए सर्वर स्थितियाँ बेहतर विकल्प हैं: वे उपयोगकर्ता के साथ हर डिवाइस पर जाती हैं और ऐप उन्हें "देखना जारी रखें" पंक्ति के लिए पढ़ सकता है। Local storage को कोड नहीं चाहिए पर एक ब्राउज़र पर रहता है और उसे इस्तेमाल करने वाले सब साझा करते हैं।
HLS और DASH
hls(id) और dash(id) एडैप्टिव स्ट्रीम जोड़ते हैं। Safari HLS नेटिव चलाता है, इसलिए वह प्लेलिस्ट इस्तेमाल करता है। अन्य ब्राउज़र को JavaScript लाइब्रेरी (जैसे hls.js या dash.js) चाहिए, जिसे Zeyn नहीं भेजता। प्रोग्रेसिव फ़ाइलों पर लौटने से पहले, प्लेयर अपनी रूट पर रद्द करने योग्य zeyn:media-adaptive इवेंट भेजता है:
document.addEventListener('zeyn:media-adaptive', (e) => {
const { media, kind, url } = e.detail;
if (kind !== 'hls' || !window.Hls?.isSupported()) return;
e.preventDefault();
const hls = new window.Hls();
hls.loadSource(url);
hls.attachMedia(media);
});वह स्क्रिप्ट अपने ऐप की फ़ाइल से लोड करें (इनलाइन स्क्रिप्ट content security policy द्वारा अवरुद्ध हैं)। जब कोई listener preventDefault() न बुलाए, file प्रविष्टियाँ क्रम से चलती हैं। प्लेलिस्ट और सेगमेंट फ़ाइलें वही स्रोत परोसता है, इसलिए उनके सापेक्ष URL तब तक काम करते हैं जब तक उनकी आईडी उसमें सूचीबद्ध हों।
अपलोड की गई फ़ाइलें
Form::file से अपलोड की गई फ़ाइलें GET /_zeyn/files/{store}/{id} द्वारा मीडिया फ़ाइलों जैसे ही उत्तरों के साथ परोसी जाती हैं (रेंज, entity टैग, nosniff, इनलाइन केवल रास्टर छवियों, ऑडियो और वीडियो के लिए), स्टोर की अनुमति, feature के गार्ड और टेनेंट जाँच के बाद। देखें examples/docs/pages/forms.md का फ़ाइल अपलोड खंड।