Apps mit Sencha Ext JS erstellen

Dieses Dokument soll Ihnen den Einstieg in die Entwicklung von Chrome-Apps mit Sencha Ext JS erleichtern. Framework. Um dieses Ziel zu erreichen, sehen wir uns eine von Sencha entwickelte Mediaplayer-App an. Die Quelle Code und API-Dokumentation sind auf GitHub verfügbar.

Diese App erkennt die verfügbaren Medienserver eines Nutzers, einschließlich der mit dem PC verbundenen Mediengeräte und die Medien über das Netzwerk verwaltet. Nutzer können Medien durchsuchen, über das Netzwerk wiedergeben oder offline.

Im Folgenden sind die wichtigsten Schritte aufgeführt, die Sie ausführen müssen, um eine Mediaplayer-App mit Sencha Ext JS zu erstellen:

  • Manifest erstellen: manifest.json.
  • Erstellen Sie die Veranstaltungsseite background.js.
  • der Sandbox-App.
  • Kommunikation zwischen der Chrome-App und Dateien, die in einer Sandbox ausgeführt werden
  • Medienserver ermitteln
  • Medien entdecken und abspielen
  • Medien offline speichern.

Manifest erstellen

Für alle Chrome-Apps ist eine Manifestdatei mit den Informationen erforderlich, die Chrome für den Start benötigt. Apps. Wie im Manifest angegeben, hat die Mediaplayer-App den Status „offline_enabled“. können Medien-Assets lokal gespeichert und unabhängig von der Internetverbindung abgerufen und wiedergegeben werden.

Die „Sandbox“ wird verwendet, um die Hauptlogik der App in einem eindeutigen Ursprung in einer Sandbox auszuführen. Alle in einer Sandbox ausgeführt Die Inhalte sind von den Content Security Policy für die Chrome App ausgenommen, können aber nicht direkt auf die APIs für Chrome-Apps Das Manifest enthält auch den "Socket" Berechtigung; verwendet die Mediaplayer-App Socket API, um über das Netzwerk eine Verbindung zu einem Medienserver herzustellen.

{
    "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"
            ]
        }
    ]
}

Seite „Termin erstellen“

Alle Chrome-Apps erfordern background.js zum Starten der Anwendung. Auf der Hauptseite des Mediaplayers index.html, wird in einem Fenster mit den angegebenen Abmessungen geöffnet:

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;
    });

});

Logik der Sandbox-App

Chrome-Apps werden in einer kontrollierten Umgebung ausgeführt, die eine strenge Content Security Policy erzwingt. (CSP). Die Mediaplayer-App benötigt höhere Berechtigungen, um die externen JS-Komponenten zu rendern. Bis CSP einhalten und die App-Logik ausführen, erstellt die Hauptseite der App (index.html) einen iFrame, der agiert als Sandbox-Umgebung:

<iframe id="sandbox-frame" sandbox="allow-scripts" src="sandbox.html"></iframe>

Der iFrame verweist auf sandbox.html. Diese Datei enthält die für das ext. JS erforderlichen Dateien. Anwendung:

<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>

Das Skript app.js führt den gesamten externen JavaScript-Code aus und rendert die Mediaplayer-Ansichten. Da diese in einer Sandbox ausgeführt wird, kann nicht direkt auf die APIs für Chrome-Apps zugegriffen werden. Kommunikation zwischen app.js und Dateien ohne Sandbox werden mit der HTML5 Post Message API erstellt.

Kommunikation zwischen Dateien

Damit die Mediaplayer-App auf Chrome-App-APIs zugreifen kann, z. B. das Netzwerk nach Medien abfragen Server sendet app.js Nachrichten an index.js. Im Gegensatz zu app.js in einer Sandbox kann index.js direkt auf die APIs für Chrome-Apps zugreifen.

index.js erstellt den iFrame:

var iframe = document.getElementById('sandbox-frame');

iframeWindow = iframe.contentWindow;

Und wartet auf Nachrichten aus den Dateien, die in einer Sandbox ausgeführt werden:

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);

Im folgenden Beispiel sendet app.js eine Nachricht an index.js, in der der Schlüssel angefordert wird "extension-baseurl":

Ext.data.PostMessage.request({
    key: 'extension-baseurl',
    success: function(data) {
        //...
    }
});

index.js empfängt die Anfrage, weist das Ergebnis zu und antwortet, indem sie die Basis-URL zurücksendet:

function extensionBaseUrl(data) {
    data.result = chrome.extension.getURL('/');
    iframeWindow.postMessage(data, '*');
}

Medienserver ermitteln

Bei der Suche nach Medienservern geht es um sehr viel. Grundsätzlich ist der Erkennungsworkflow der durch eine Nutzeraktion initiiert wurde, um nach verfügbaren Medienservern zu suchen. Den MediaServer-Controller postet eine Nachricht an index.js; index.js wartet auf diese Nachricht. Bei Eingang werden Anrufe entgegengenommen Upnp.js:

Der Upnp library verwendet die Socket API der Chrome App, um die Mediaplayer-App mit einem beliebigen erkannte Medienserver und empfangen Mediendaten vom Medienserver. Upnp.js verwendet auch soapclient.js zum Parsen der Medienserverdaten. Im weiteren Verlauf dieses Abschnitts wird dies auf den Workflow eingeht.

Nachricht posten

Wenn ein Nutzer in der Mitte der Mediaplayer-App auf die Schaltfläche „Medienserver“ klickt, MediaServers.js ruft discoverServers() auf. Diese Funktion prüft zuerst, ob ausstehende Erkennungsanfragen vorliegen. true, bricht sie ab, sodass die neue Anfrage initiiert werden kann. Als Nächstes sendet der Controller eine Nachricht an index.js mit einem upnp-Discovery-Schlüssel und zwei Callback-Listenern:

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');
    }
});

upnpDiscover() aufrufen

index.js wartet auf „upnp-discover“ Nachricht von app.js und antwortet mit einem Anruf upnpDiscover() Wenn ein Medienserver erkannt wird, extrahiert index.js die Medienserverdomain aus den Parametern, speichert den Server lokal, formatiert die Medienserverdaten und überträgt die Daten per Push den MediaServer-Controller.

Medienserverdaten parsen

Wenn Upnp.js einen neuen Medienserver erkennt, ruft er eine Beschreibung des Geräts ab und sendet eine SOAP-Anfrage zum Suchen und Parsen der Medienserverdaten; soapclient.js parst die Medienelemente nach Tag-Namen in ein Dokument einfügen.

Verbindung zum Medienserver herstellen

Upnp.js stellt eine Verbindung zu erkannten Medienservern her und empfängt Mediendaten über den Chrome App-Socket 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);
        });
    });
});

Medien entdecken und abspielen

Der MediaExplorer-Controller listet alle Mediendateien in einem Medienserverordner auf und wird ist für die Aktualisierung des Navigationspfads im Fenster der Mediaplayer-App verantwortlich. Wenn ein Nutzer eine Mediendatei auswählt und der Controller eine Nachricht mit „play-media“ an index.js sendet Schlüssel:

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 wartet auf diese Nachricht und antwortet mit 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);
}

Medien offline speichern

Die Offlinespeicherung von Medien wird größtenteils von der filer.js-Bibliothek übernommen. Weitere Informationen im Artikel Einführung in filer.js lesen.

Der Prozess wird gestartet, wenn ein Nutzer eine oder mehrere Dateien auswählt und den Offlinezugriff einleitet. Aktion ausführen. Der MediaExplorer-Controller sendet eine Nachricht mit dem Schlüssel 'download-media' an index.js. index.js wartet auf diese Nachricht und ruft die Funktion downloadMedia() auf, um den Download-Prozess:

function downloadMedia(data) {
        DownloadProcess.run(data.params.files, function() {
            data.result = true;
            sendMessage(data);
        });
    }

Die Methode DownloadProcess des Dienstprogramms erstellt eine xhr-Anfrage zum Abrufen von Daten vom Medienserver und wartet auf den Abschlussstatus. Dadurch wird der Onload-Callback initiiert, der den empfangenen Inhalt prüft. und speichert die Daten lokal mit der Funktion 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);
    }
);

Wenn der Download abgeschlossen ist, aktualisiert MediaExplorer die Liste der Mediendateien und die Mediendatei Baumansicht des Spielers.