Crea app con Sencha Ext JS

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.