Créer des applications avec Sencha Ext JS

L'objectif de ce document est de vous aider à créer des applications Chrome avec Sencha Ext JS. d'infrastructure. Pour atteindre cet objectif, nous allons découvrir une application de lecteur multimédia conçue par Sencha. La source du code et la documentation de l'API sont disponibles sur GitHub.

Cette appli détecte les serveurs multimédias disponibles d'un utilisateur, y compris les appareils multimédias connectés à un PC et des logiciels qui gèrent les contenus multimédias sur le réseau. Les utilisateurs peuvent parcourir les contenus multimédias, les lire via le réseau ou enregistrer hors connexion.

Voici les principales étapes à suivre pour créer une application de lecteur multimédia à l'aide de Sencha Ext JS:

  • Créez le fichier manifeste manifest.json.
  • Créez la page de l'événement background.js.
  • La logique d'une application bac à sable.
  • Communication entre l'application Chrome et les fichiers en bac à sable
  • Découvrir les serveurs multimédias
  • Explorez et lisez les contenus multimédias.
  • Enregistrer des contenus multimédias hors connexion

Créer un fichier manifeste

Toutes les applications Chrome nécessitent un fichier manifeste contenant les informations nécessaires au lancement de Chrome. applications. Comme indiqué dans le fichier manifeste, l'application de lecteur multimédia est "offline_enabled" (hors connexion). les assets multimédias peuvent être enregistrées localement, consultées et lues quelle que soit la connectivité.

Le "bac à sable" est utilisé pour mettre en bac à sable la logique principale de l'application dans une origine unique. En bac à sable est exempté du Content Security Policy de l'application Chrome, mais ne peut pas y accéder directement. API Chrome App. Le fichier manifeste inclut également le "socket" Autorisation l'application de lecture multimédia utilise API de socket pour se connecter à un serveur de contenus sur le réseau.

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

Page "Créer un événement"

Toutes les applications Chrome nécessitent background.js pour lancer l'application. La page principale du lecteur multimédia index.html s'ouvre dans une fenêtre avec les dimensions spécifiées:

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

});

Logique de l'application sandbox

Les applications Chrome s'exécutent dans un environnement contrôlé qui applique une Content Security Policy stricte (CSP). L'application de lecteur multimédia nécessite des droits plus élevés pour afficher les composants JS ext. À est conforme à CSP et exécute la logique de l'application, la page principale de l'application, index.html, qui crée un iFrame qui agit comme un environnement de bac à sable:

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

L'iFrame pointe vers sandbox.html, qui inclut les fichiers requis pour l'extension JS application:

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

Le script app.js exécute tout le code JS ext et affiche les vues du lecteur multimédia. Étant donné que est exécuté en bac à sable, il ne peut pas accéder directement aux API Chrome App. Communication entre app.js et hors bac à sable se fait à l'aide de l'API HTML5 Post Message.

Communiquer entre les fichiers

Pour que l'application de lecture multimédia puisse accéder aux API de l'application Chrome (par exemple, interroger le réseau pour des contenus multimédias) serveurs, app.js publie des messages dans index.js. Contrairement au app.js en bac à sable, index.js peut accéder directement aux API Chrome App.

index.js crée l'iFrame:

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

iframeWindow = iframe.contentWindow;

Il écoute également les messages des fichiers en bac à sable:

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

Dans l'exemple suivant, app.js envoie un message à index.js demandant la clé. 'extension-baseurl':

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

index.js reçoit la requête, attribue le résultat et répond en renvoyant l'URL de base:

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

Découvrir des serveurs multimédias

Il y a beaucoup de choses qui entrent dans la découverte des serveurs multimédias. De manière générale, le workflow de découverte initiée par une action de l'utilisateur pour rechercher les serveurs multimédias disponibles. Le contrôleur MediaServer publie un message à l'adresse index.js ; index.js écoute ce message et appelle Upnp.js :

Le Upnp library utilise l'API de socket de l'application Chrome pour connecter l'application de lecteur multimédia aux les serveurs de médias découverts et reçoivent des données multimédias du serveur. Upnp.js utilise aussi soapclient.js pour analyser les données du serveur multimédia Le reste de cette section décrit ce processus le workflow de ML.

Publier le message

Lorsqu'un utilisateur clique sur le bouton "Serveurs multimédias" au centre de l'application de lecture multimédia, MediaServers.js appelle discoverServers(). Cette fonction vérifie d'abord les demandes de découverte en attente, et si true, les annule pour que la nouvelle requête puisse être lancée. Ensuite, le contrôleur publie un message sur index.js avec une clé upnp-discovery et deux écouteurs de rappel:

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

Appeler upnpDiscover()

index.js écoute l'événement "upnp-discover". de app.js et répond en appelant upnpDiscover() Lorsqu'un serveur multimédia est détecté, index.js extrait le domaine du serveur multimédia des paramètres, enregistre le serveur localement, met en forme les données du serveur multimédia et les transmet le contrôleur MediaServer.

Analyser les données du serveur multimédia

Lorsque Upnp.js détecte un nouveau serveur multimédia, il récupère une description de l'appareil et l'envoie une requête Soaprequest pour parcourir et analyser les données du serveur multimédia ; soapclient.js analyse les éléments multimédias par nom de tag dans un document.

Se connecter au serveur multimédia

Upnp.js se connecte aux serveurs multimédias détectés et reçoit les données multimédias à l'aide du socket d'application 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);
        });
    });
});

Explorer et lire des contenus multimédias

Le contrôleur MediaExplorer répertorie tous les fichiers multimédias d'un dossier de serveur multimédia et est qui est chargé de mettre à jour la navigation dans le fil d'Ariane dans la fenêtre de l'application du lecteur multimédia. Lorsqu'un utilisateur sélectionne un fichier multimédia, la manette publie un message sur index.js avec l'étiquette "play-media" clé:

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 écoute ce message et répond en appelant 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);
}

Enregistrer des contenus multimédias hors connexion

L'enregistrement de contenus multimédias hors connexion est principalement effectué par la bibliothèque filer.js. Pour en savoir plus, cette bibliothèque dans l'article Présentation de filer.js.

Le processus est lancé lorsqu'un utilisateur sélectionne un ou plusieurs fichiers et lance l'action "Hors connexion". action. Le contrôleur MediaExplorer publie un message sur index.js avec une clé "download-media". index.js écoute ce message et appelle la fonction downloadMedia() pour lancer la processus de téléchargement:

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

La méthode utilitaire DownloadProcess crée une requête xhr pour obtenir les données du serveur multimédia. attend l'état d'avancement. Cela lance le rappel de chargement qui vérifie le contenu reçu et enregistre les données localement à l'aide de la fonction 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);
    }
);

Une fois le téléchargement terminé, MediaExplorer met à jour la liste des fichiers multimédias et les contenus multimédias dans l'arborescence des joueurs.