इस दस्तावेज़ का मकसद, Sencha Ext JS फ़्रेमवर्क के साथ, Chrome ऐप्लिकेशन बनाने में आपकी मदद करना है. इस लक्ष्य को हासिल करने के लिए, हम Sencha के बनाए मीडिया प्लेयर ऐप्लिकेशन के बारे में जानेंगे. सोर्स कोड और एपीआई दस्तावेज़, GitHub पर उपलब्ध हैं.
यह ऐप्लिकेशन उपयोगकर्ता के उपलब्ध मीडिया सर्वर का पता लगाता है, जिनमें पीसी से कनेक्ट किए गए मीडिया डिवाइस और नेटवर्क पर मीडिया मैनेज करने वाले सॉफ़्टवेयर शामिल होते हैं. उपयोगकर्ता मीडिया ब्राउज़ कर सकते हैं, नेटवर्क पर चला सकते हैं या ऑफ़लाइन सेव कर सकते हैं.
Sencha Ext JS का इस्तेमाल करके, मीडिया प्लेयर ऐप्लिकेशन बनाने के लिए आपको ये ज़रूरी काम करने होंगे:
- मेनिफ़ेस्ट बनाएं,
manifest.json
. - इवेंट पेज बनाएं,
background.js
. - Sandbox ऐप्लिकेशन का लॉजिक.
- Chrome ऐप्लिकेशन और सैंडबॉक्स की गई फ़ाइलों के बीच बातचीत करें.
- मीडिया सर्वर डिस्कवर करें.
- मीडिया एक्सप्लोर करें और चलाएं.
- मीडिया को ऑफ़लाइन सेव करें.
मेनिफ़ेस्ट बनाएं
सभी Chrome ऐप्स को एक मेनिफ़ेस्ट फ़ाइल की आवश्यकता होती है जिसमें वह जानकारी होती है जिसकी Chrome को ऐप्लिकेशन लॉन्च करने के लिए आवश्यकता होती है. जैसा कि मेनिफ़ेस्ट में बताया गया है, मीडिया प्लेयर ऐप्लिकेशन "offline_enabled" है; मीडिया एसेट को डिवाइस में सेव किया जा सकता है, ऐक्सेस किया जा सकता है, और कनेक्टिविटी पर ध्यान दिए बिना चलाया जा सकता है.
"सैंडबॉक्स" फ़ील्ड का इस्तेमाल, ऐप्लिकेशन के मुख्य लॉजिक को किसी यूनीक ऑरिजिन से सैंडबॉक्स करने के लिए किया जाता है. सैंडबॉक्स किए गए सभी कॉन्टेंट को Chrome ऐप्लिकेशन की कॉन्टेंट की सुरक्षा नीति से छूट मिली हुई है. हालांकि, इससे सीधे Chrome App API को ऐक्सेस नहीं किया जा सकता. मेनिफ़ेस्ट में "सॉकेट" अनुमति भी शामिल है. मीडिया प्लेयर ऐप्लिकेशन, नेटवर्क पर किसी मीडिया सर्वर से कनेक्ट करने के लिए, socket API का इस्तेमाल करता है.
{
"name": "Video Player",
"description": "Features network media discovery and playlist management",
"version": "1.0.0",
"manifest_version": 2,
"offline_enabled": true,
"app": {
"background": {
"scripts": [
"background.js"
]
}
},
...
"sandbox": {
"pages": ["sandbox.html"]
},
"permissions": [
"experimental",
"http://*/*",
"unlimitedStorage",
{
"socket": [
"tcp-connect",
"udp-send-to",
"udp-bind"
]
}
]
}
इवेंट पेज बनाएं
सभी Chrome ऐप्स में ऐप्लिकेशन लॉन्च करने के लिए background.js
ज़रूरी है. मीडिया प्लेयर का मुख्य पेज,
index.html
, दिए गए डाइमेंशन के साथ एक विंडो में खुलता है:
chrome.app.runtime.onLaunched.addListener(function(launchData) {
var opt = {
width: 1000,
height: 700
};
chrome.app.window.create('index.html', opt, function (win) {
win.launchData = launchData;
});
});
सैंडबॉक्स ऐप्लिकेशन का लॉजिक
Chrome ऐप्स ऐसे एनवायरमेंट में चलते हैं जो कॉन्टेंट की सुरक्षा के बारे में सख्त नीति (सीएसपी) को लागू करते हैं. Ext JS कॉम्पोनेंट को रेंडर करने के लिए, मीडिया प्लेयर ऐप्लिकेशन को कुछ ज़्यादा खास अधिकारों की ज़रूरत होती है. सीएसपी का पालन करने और ऐप्लिकेशन लॉजिक को एक्ज़ीक्यूट करने के लिए, ऐप्लिकेशन का मुख्य पेज index.html
एक iframe बनाता है, जो सैंडबॉक्स एनवायरमेंट की तरह काम करता है:
<iframe id="sandbox-frame" sandbox="allow-scripts" src="sandbox.html"></iframe>
यह iframe sandbox.html पर ले जाता है. इसमें Ext JS ऐप्लिकेशन के लिए ज़रूरी फ़ाइलें शामिल होती हैं:
<html>
<head>
<link rel="stylesheet" type="text/css" href="resources/css/app.css" />'
<script src="sdk/ext-all-dev.js"></script>'
<script src="lib/ext/data/PostMessage.js"></script>'
<script src="lib/ChromeProxy.js"></script>'
<script src="app.js"></script>
</head>
<body></body>
</html>
app.js स्क्रिप्ट, सभी Ext JS कोड को एक्ज़ीक्यूट करती है और मीडिया प्लेयर व्यू को रेंडर करती है. इस स्क्रिप्ट को सैंडबॉक्स किया गया है, इसलिए यह सीधे Chrome App API को ऐक्सेस नहीं कर सकता. app.js
और
सैंडबॉक्स नहीं की गई फ़ाइलों के बीच कम्यूनिकेशन, HTML5 Post Message API का इस्तेमाल करके किया जाता है.
फ़ाइलों के बीच बातचीत करना
मीडिया प्लेयर ऐप्लिकेशन, Chrome ऐप्लिकेशन एपीआई को ऐक्सेस कर सके, इसके लिए app.js
, index.js पर मैसेज पोस्ट करता है. जैसे, मीडिया सर्वर के लिए नेटवर्क की क्वेरी करना. सैंडबॉक्स किए गए app.js
के उलट, index.js
सीधे Chrome ऐप्लिकेशन एपीआई को ऐक्सेस कर सकता है.
index.js
iframe बनाता है:
var iframe = document.getElementById('sandbox-frame');
iframeWindow = iframe.contentWindow;
और सैंडबॉक्स की गई फ़ाइलों के मैसेज सुनता है:
window.addEventListener('message', function(e) {
var data= e.data,
key = data.key;
console.log('[index.js] Post Message received with key ' + key);
switch (key) {
case 'extension-baseurl':
extensionBaseUrl(data);
break;
case 'upnp-discover':
upnpDiscover(data);
break;
case 'upnp-browse':
upnpBrowse(data);
break;
case 'play-media':
playMedia(data);
break;
case 'download-media':
downloadMedia(data);
break;
case 'cancel-download':
cancelDownload(data);
break;
default:
console.log('[index.js] unidentified key for Post Message: "' + key + '"');
}
}, false);
इस उदाहरण में, app.js
index.js
को एक मैसेज भेजता है जिसमें 'extension-baseurl' कुंजी का अनुरोध किया जाता है:
Ext.data.PostMessage.request({
key: 'extension-baseurl',
success: function(data) {
//...
}
});
index.js
को अनुरोध मिलता है, वह नतीजा असाइन करता है, और मूल यूआरएल को वापस भेजकर जवाब देता है:
function extensionBaseUrl(data) {
data.result = chrome.extension.getURL('/');
iframeWindow.postMessage(data, '*');
}
मीडिया सर्वर डिस्कवर करें
मीडिया सर्वर खोजने में बहुत कुछ करना पड़ता है. बड़े लेवल पर, डिस्कवरी वर्कफ़्लो, उपलब्ध मीडिया सर्वर खोजने के लिए उपयोगकर्ता की कार्रवाई से शुरू होता है. MediaServer कंट्रोलर, index.js
पर मैसेज पोस्ट करता है. index.js
इस मैसेज को सुनता है और कॉल मिलने पर
Upnp.js को कॉल करता है.
Upnp library
, मीडिया प्लेयर ऐप्लिकेशन को खोजे गए किसी भी मीडिया सर्वर से कनेक्ट करने और मीडिया सर्वर से मीडिया डेटा पाने के लिए, Chrome ऐप्लिकेशन सॉकेट एपीआई का इस्तेमाल करता है. Upnp.js
, मीडिया सर्वर के डेटा को पार्स करने के लिए,
soapclient.js का भी इस्तेमाल करता है. इस सेक्शन के बाकी हिस्से में, इस वर्कफ़्लो के बारे में ज़्यादा जानकारी दी गई है.
मैसेज पोस्ट करें
जब कोई उपयोगकर्ता मीडिया प्लेयर ऐप्लिकेशन के बीच में मौजूद मीडिया सर्वर बटन पर क्लिक करता है, तो MediaServers.js
discoverServers()
को कॉल करता है. यह फ़ंक्शन पहले किसी भी बकाया खोज अनुरोध की जांच करता है. अगर
सही हो, तो उन्हें रद्द कर देता है, ताकि नया अनुरोध किया जा सके. इसके बाद, कंट्रोलर
index.js
के लिए एक मैसेज पोस्ट करता है, जिसमें एक कुंजी-अप-डिस्कवरी और दो कॉलबैक लिसनर होते हैं:
me.activeDiscoverRequest = Ext.data.PostMessage.request({
key: 'upnp-discover',
success: function(data) {
var items = [];
delete me.activeDiscoverRequest;
if (serversGraph.isDestroyed) {
return;
}
mainBtn.isLoading = false;
mainBtn.removeCls('pop-in');
mainBtn.setIconCls('ico-server');
mainBtn.setText('Media Servers');
//add servers
Ext.each(data, function(server) {
var icon,
urlBase = server.urlBase;
if (urlBase) {
if (urlBase.substr(urlBase.length-1, 1) === '/'){
urlBase = urlBase.substr(0, urlBase.length-1);
}
}
if (server.icons && server.icons.length) {
if (server.icons[1]) {
icon = server.icons[1].url;
}
else {
icon = server.icons[0].url;
}
icon = urlBase + icon;
}
items.push({
itemId: server.id,
text: server.friendlyName,
icon: icon,
data: server
});
});
...
},
failure: function() {
delete me.activeDiscoverRequest;
if (serversGraph.isDestroyed) {
return;
}
mainBtn.isLoading = false;
mainBtn.removeCls('pop-in');
mainBtn.setIconCls('ico-error');
mainBtn.setText('Error...click to retry');
}
});
upnpडिस्कवर() को कॉल करें
index.js
, app.js
से 'upnp-discover' मैसेज को सुनता है और
upnpDiscover()
पर कॉल करके जवाब देता है. मीडिया सर्वर मिलने पर, index.js
पैरामीटर से मीडिया सर्वर डोमेन को निकालता है, सर्वर को स्थानीय रूप से सेव करता है, मीडिया सर्वर डेटा को फ़ॉर्मैट करता है, और डेटा को MediaServer
कंट्रोलर में पुश करता है.
मीडिया सर्वर के डेटा को पार्स करें
जब Upnp.js
को कोई नया मीडिया सर्वर मिलता है, तो वह डिवाइस की जानकारी हासिल करता है और मीडिया सर्वर के डेटा को ब्राउज़ और पार्स करने के लिए, Soaprequest भेजता है. soapclient.js
, टैग के नाम से मीडिया एलिमेंट को दस्तावेज़ में पार्स करता है.
मीडिया सर्वर से कनेक्ट करें
Upnp.js
, खोजे गए मीडिया सर्वर से कनेक्ट करता है और Chrome ऐप्लिकेशन सॉकेट एपीआई का इस्तेमाल करके, मीडिया डेटा पाता है:
socket.create("udp", {}, function(info) {
var socketId = info.socketId;
//bind locally
socket.bind(socketId, "0.0.0.0", 0, function(info) {
//pack upnp message
var message = String.toBuffer(UPNP_MESSAGE);
//broadcast to upnp
socket.sendTo(socketId, message, UPNP_ADDRESS, UPNP_PORT, function(info) {
// Wait 1 second
setTimeout(function() {
//receive
socket.recvFrom(socketId, function(info) {
//unpack message
var data = String.fromBuffer(info.data),
servers = [],
locationReg = /^location:/i;
//extract location info
if (data) {
data = data.split("\r\n");
data.forEach(function(value) {
if (locationReg.test(value)){
servers.push(value.replace(locationReg, "").trim());
}
});
}
//success
callback(servers);
});
}, 1000);
});
});
});
मीडिया एक्सप्लोर करें और चलाएं
Mediaएक्सप्लोरर कंट्रोलर की मदद से, मीडिया सर्वर फ़ोल्डर के अंदर मौजूद सभी मीडिया फ़ाइलों की सूची बनाई जाती है. साथ ही, इसकी ज़िम्मेदारी मीडिया प्लेयर ऐप्लिकेशन विंडो में ब्रेडक्रंब नेविगेशन को अपडेट करने की होती है. जब कोई मीडिया फ़ाइल चुनता है, तो कंट्रोलर 'प्ले-मीडिया' बटन के साथ index.js
पर मैसेज पोस्ट करता है:
onFileDblClick: function(explorer, record) {
var serverPanel, node,
type = record.get('type'),
url = record.get('url'),
name = record.get('name'),
serverId= record.get('serverId');
if (type === 'audio' || type === 'video') {
Ext.data.PostMessage.request({
key : 'play-media',
params : {
url: url,
name: name,
type: type
}
});
}
},
index.js
, इस पोस्ट के मैसेज को सुनता है और playMedia()
को कॉल करके जवाब देता है:
function playMedia(data) {
var type = data.params.type,
url = data.params.url,
playerCt = document.getElementById('player-ct'),
audioBody = document.getElementById('audio-body'),
videoBody = document.getElementById('video-body'),
mediaEl = playerCt.getElementsByTagName(type)[0],
mediaBody = type === 'video' ? videoBody : audioBody,
isLocal = false;
//save data
filePlaying = {
url : url,
type: type,
name: data.params.name
};
//hide body els
audioBody.style.display = 'none';
videoBody.style.display = 'none';
var animEnd = function(e) {
//show body el
mediaBody.style.display = '';
//play media
mediaEl.play();
//clear listeners
playerCt.removeEventListener( 'transitionend', animEnd, false );
animEnd = null;
};
//load media
mediaEl.src = url;
mediaEl.load();
//animate in player
playerCt.addEventListener( 'transitionend', animEnd, false );
playerCt.style.transform = "translateY(0)";
//reply postmessage
data.result = true;
sendMessage(data);
}
मीडिया को ऑफ़लाइन सेव करें
मीडिया को ऑफ़लाइन सेव करने की ज़्यादातर मेहनत filer.js लाइब्रेरी से मिलती है. इस लाइब्रेरी के बारे में ज़्यादा जानकारी पेश है filer.js से.
प्रोसेस तब शुरू होती है, जब कोई उपयोगकर्ता एक या उससे ज़्यादा फ़ाइलें चुनता है और 'ऑफ़लाइन ले जाएं' कार्रवाई शुरू करता है.
Mediaएक्सप्लोरर कंट्रोलर 'डाउनलोड-मीडिया' कुंजी के साथ, index.js
को एक मैसेज पोस्ट करता है;
index.js
इस मैसेज को सुनता है और डाउनलोड की प्रोसेस शुरू करने के लिए, downloadMedia()
फ़ंक्शन को कॉल करता है:
function downloadMedia(data) {
DownloadProcess.run(data.params.files, function() {
data.result = true;
sendMessage(data);
});
}
DownloadProcess
यूटिलिटी तरीके, मीडिया सर्वर से डेटा पाने के लिए एक xhr अनुरोध बनाता है और प्रोसेस पूरी होने की स्थिति का इंतज़ार करता है. यह ऑनलोड कॉलबैक शुरू करता है, जो मिलने वाले कॉन्टेंट की जांच करता है और filer.js
फ़ंक्शन का इस्तेमाल करके, डेटा को स्थानीय तौर पर सेव करता है:
filer.write(
saveUrl,
{
data: Util.arrayBufferToBlob(fileArrayBuf),
type: contentType
},
function(fileEntry, fileWriter) {
console.log('file saved!');
//increment downloaded
me.completedFiles++;
//if reached the end, finalize the process
if (me.completedFiles === me.totalFiles) {
sendMessage({
key : 'download-progresss',
totalFiles : me.totalFiles,
completedFiles : me.completedFiles
});
me.completedFiles = me.totalFiles = me.percentage = me.downloadedFiles = 0;
delete me.percentages;
//reload local
loadLocalFiles(callback);
}
},
function(e) {
console.log(e);
}
);
डाउनलोड की प्रोसेस पूरी होने पर, MediaExplorer
मीडिया फ़ाइल की सूची और मीडिया प्लेयर ट्री पैनल को अपडेट करता है.