L'obiettivo di questo documento è iniziare a creare app di Chrome con Sencha Ext JS il modello di machine learning. Per raggiungere questo obiettivo, esamineremo in dettaglio un'app di media player creata da Sencha. La fonte e la documentazione delle API sono disponibili su GitHub.
Questa app rileva i server multimediali disponibili di un utente, inclusi i dispositivi multimediali connessi al PC e software che gestisce i contenuti multimediali sulla rete. Gli utenti possono sfogliare i contenuti multimediali, riprodurli sulla rete o salvare offline.
Di seguito sono riportati gli aspetti fondamentali per sviluppare un'app di media player utilizzando Sencha Ext JS:
- Crea il manifest,
manifest.json
. - Crea una pagina dell'evento,
background.js
. - Logica dell'app sandbox.
- Comunicazione tra l'app di Chrome e i file con sandbox.
- Scopri i server multimediali.
- Esplora e riproduci contenuti multimediali.
- Salva i contenuti multimediali offline.
Crea manifest
Tutte le app di Chrome richiedono un file manifest contenente le informazioni necessarie per Chrome app. Come indicato nel file manifest, l'app del media player è "offline_enabled"; Gli asset multimediali possono essere salvate localmente, consultate e riprodotte indipendentemente dalla connettività.
La "sandbox" viene utilizzato per limitare la logica principale dell'app a un'origine unica. Tutti i contenuti sandbox sono esenti dal Criterio di sicurezza del contenuto dell'app Chrome, ma non possono accedere direttamente ai le API per le app di Chrome. Il manifest include anche il "socket" autorizzazione; l'app del media player utilizza API socket per connettersi a un server multimediale sulla rete.
{
"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"
]
}
]
}
Pagina Crea evento
Tutte le app di Chrome richiedono background.js
per poter avviare l'applicazione. Nella pagina principale del media player
index.html
, si apre in una finestra con le dimensioni specificate:
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;
});
});
Logica dell'app Sandbox
Le app di Chrome vengono eseguite in un ambiente controllato che applica un rigoroso criterio di sicurezza del contenuto
(CSP). L'app del media player richiede privilegi più elevati per il rendering dei componenti Ext JS. A
rispettare CSP ed eseguire la logica dell'app; la pagina principale dell'app, index.html
, crea un iframe che
funge da ambiente sandbox:
<iframe id="sandbox-frame" sandbox="allow-scripts" src="sandbox.html"></iframe>
L'iframe rimanda a sandbox.html, che include i file richiesti per il file JavaScript esterno applicazione:
<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>
Lo script app.js esegue tutto il codice Ext JS ed esegue il rendering delle visualizzazioni del media player. Dal momento che
è limitato tramite sandbox e non può accedere direttamente alle API dell'app Chrome. Comunicazione tra app.js
e i file senza sandbox si effettuano utilizzando l'API HTML5 Post Message.
Comunica tra i file
Per consentire all'app del media player di accedere alle API dell'app Chrome, ad esempio inviare una query alla rete per trovare contenuti multimediali.
server, app.js
pubblica messaggi su index.js. A differenza dell'app.js
con sandbox, index.js
può
di accedere direttamente alle API dell'app Chrome.
index.js
crea l'iframe:
var iframe = document.getElementById('sandbox-frame');
iframeWindow = iframe.contentWindow;
Inoltre rimane in ascolto dei messaggi provenienti dai file sandbox:
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);
Nell'esempio seguente, app.js
invia un messaggio a index.js
richiedendo la chiave
"extension-baseurl":
Ext.data.PostMessage.request({
key: 'extension-baseurl',
success: function(data) {
//...
}
});
index.js
riceve la richiesta, assegna il risultato e risponde inviando l'URL di base:
function extensionBaseUrl(data) {
data.result = chrome.extension.getURL('/');
iframeWindow.postMessage(data, '*');
}
Scopri i server multimediali
Il processo di scoperta dei server multimediali richiede molto impegno. A livello generale, il flusso di lavoro
di rilevamento
avviata da un'azione utente per cercare server multimediali disponibili. Il controller MediaServer
pubblica un messaggio in index.js
; index.js
ascolta questo messaggio e, alla ricezione, chiama
Upnp.js:
Upnp library
utilizza l'API socket dell'app Chrome per connettere l'app del media player a qualsiasi
ha rilevato i server multimediali e ricevono dati multimediali dal server. Upnp.js
usa anche
soapclient.js per analizzare i dati del server multimediale. Il resto di questa sezione descrive
flusso di lavoro in modo più dettagliato.
Pubblica messaggio
Quando un utente fa clic sul pulsante Server multimediali al centro dell'app del lettore multimediale, MediaServers.js
chiama discoverServers()
. Questa funzione controlla innanzitutto se ci sono richieste di rilevamento in sospeso e se
true, le interrompe in modo che possa essere avviata la nuova richiesta. Quindi, il controller pubblica un messaggio
index.js
con una chiave upnp-discovery e due listener di callback:
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');
}
});
Richiama upnpDiscover()
index.js
ascolta "upnp-discover" messaggio di app.js
e risponde chiamando
upnpDiscover()
. Quando viene rilevato un server multimediale, index.js
estrae il dominio del server multimediale.
dai parametri, salva il server localmente, formatta i dati del server multimediale ed esegue il push dei dati al
il controller MediaServer
.
Analizza i dati del server multimediale
Quando Upnp.js
rileva un nuovo server multimediale, recupera una descrizione del dispositivo e invia
una richiesta Soaprequest per sfogliare e analizzare i dati del server multimediale; soapclient.js
analizza gli elementi multimediali
per nome tag in un documento.
Connettiti al server multimediale
Upnp.js
si connette ai server multimediali rilevati e riceve dati multimediali tramite il socket dell'app Chrome
API:
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);
});
});
});
Esplora e riproduci contenuti multimediali
Il controller MediaExplorer elenca tutti i file multimediali all'interno di una cartella del server multimediale
responsabile dell'aggiornamento della navigazione dei breadcrumb nella finestra dell'app del media player. Quando un utente
seleziona un file multimediale, il controller pubblica un messaggio per index.js
con il "play-media" chiave:
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
ascolta questo messaggio del post e risponde chiamando il numero 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);
}
Salva contenuti multimediali offline
La maggior parte del lavoro duro per salvare contenuti multimediali offline viene svolta dalla libreria filer.js. Puoi scoprire di più questa libreria nell'articolo Introduzione a filer.js.
Il processo si avvia quando un utente seleziona uno o più file e avvia il processo "Take offline" un'azione.
Il controller MediaExplorer pubblica un messaggio per index.js
con la chiave "download-media";
index.js
rimane in ascolto di questo messaggio e chiama la funzione downloadMedia()
per avviare la
procedura di download:
function downloadMedia(data) {
DownloadProcess.run(data.params.files, function() {
data.result = true;
sendMessage(data);
});
}
Il metodo di utilità DownloadProcess
crea una richiesta xhr per ottenere i dati dal server multimediale e
attende lo stato di completamento. Questa operazione avvia il callback onload che controlla i contenuti ricevuti
e salva i dati localmente utilizzando la funzione 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);
}
);
Al termine del processo di download, MediaExplorer
aggiorna l'elenco dei file multimediali e i contenuti multimediali
riquadro ad albero dei player.