Pour explorer Electron, nous allons créer une application de bureau qui va nous donner la durée d'une vidéo. Nous allons pouvoir regarder comment:
Tout d'abord, créons un dossier pour notre projet et appelons le longueurvideo
. Ouvrez ensuite votre IDE (vscode) dans ce dossier.
N'oublions pas que nous allons coder en Javascript et que notre projet sera assemblé par Nodejs. Assurez vous d'avoir node installé sur votre machine en tapant dans un terminal
node -v
. Ainsi que npm ou yarn qui sont des gestionnaires de dépendancesnpm -v
.
Dans le dossier longueurvideo
, initialisons le projet à l'aide de la commande npm init
.
npm va vous poser plusieurs questions que nous allons laisser par défaut pour le moment.
npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (longueurvideo)
version: (1.0.0)
description: premier exercice
entry point: (index.js)
test command:
git repository:
keywords:
author: germain
license: (ISC)
About to write to /Users/germain/developpement/pourCours/longueurvideo/package.json:
{
"name": "longueurvideo",
"version": "1.0.0",
"description": "premier exercice",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "germain",
"license": "ISC"
}
Is this OK? (yes)
Nous avons maintenant besoin d'Electron. Installons le comme dépendance:
npm install electron --save-dev
Comme vous l'avez compris, nous allons avoir une partie "web" et une partie "system". La partie web sera un fichier html dans un premier temps et l'autre sera un fichier js pour démarrer notre Electron.
Commençons par le js en créant un fichier index.js
longueurvideo
├── index.js
├── node_modules
├── package-lock.json
└── package.json
dans ce ìndex.js
nous allons déclarer electron comme dépendance avec:
const electron = require('electron')
ici pas d'
import
pour ceux qui en ont l'habitude mais bien unrequire
car c'est nodejs qui est notre environnement.
Cet import va nous servir a créer des fenêtre et interagir avec le système hôte. Ce n'est pas electron qui gère l'interface utilisateur.
Dans ce package electron ce qui va d'abord nous interesser c'est la propriété app
.
const { app } = electron
Quand nous lançons une application electron voilà ce qu'il se passe:
Testons ça avec dans notre fichier index.js
const electron = require('electron')
const { app } = require('electron')
app.on('ready', () => {
})
Le app.on('ready', () => {})
attache, en fait, un écouteur à app
. Lorsque l’évènement ready arrive alors la méthode fléché qui suit se déclenche. D'autres évènements existent et sont listés ici.
Remarque: vous pourrez aussi utiliser
app.whenReady().then( () => {} )
Pour nous simplifier la vie, ajoutons un script pour démarrer electron et lancer l'application en developpement.
dans package.json
ajoutez "start": "electron ."
dans la section "script":{ ... }
{
"name": "longueurvideo",
"version": "1.0.0",
"description": "premier exercice",
"main": "index.js",
"scripts": {
"start": "electron .", <-- ici
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "germain",
"license": "ISC",
"devDependencies": {
"electron": "^16.0.7"
}
}
Maintenant si vous tapez npm start
dans le terminal vous devriez obtenir:
❯ npm start
> longueurvideo@1.0.0 start
> electron .
app est pret !
Pas de fenêtres, mais un processus lancé. Pour l’arrêter je vous conseil de faire ctrl+c
dans le terminal pour le stopper et stopper ainsi l'application.
Comme nous l'avons dit plus tôt dans Chrome il y a des onglet et pour Electron ce sont des fenêtres (BrowserWindow
). Nous devons déclarer cette fenêtre nous même. Faisons ça dans une méthode et appelons la méthode dans app.on('ready'...
:
const electron = require('electron')
const { app, BrowserWindow } = require('electron')
// fonction de création de fenêtres
// n'oubliez pas l'import de BrowserWindow
function createWindow() {
const win = new BrowserWindow({})
}
app.on('ready', () => {
console.log("app est pret !")
createWindow() // <- ici
})
Si vous lancez l'application vous devriez avoir une fenêtre.
Pour pouvoir afficher quelque chose il va nous falloir un "front". Ici un fichier html. Je ne vais pas m'occuper du style pour le moment.
Ajoutons un fichier index.html
et remplissons le comme ceci:
<head>
</head>
<body>
<h1>Durée des Vidéos</h1>
</body>
Et disons à la fenêtre de charger notre page
function createWindow() {
const win = new BrowserWindow({})
win.loadFile('index.html') // <--ici
}
Remarque: J'utilise
loadFile
mais vous trouverez aussiloadURL
car Electron est un navigateur. Dans notre cas la méthode est plus adaptée car elle va d'elle même ecrire le chemin vers le fichier ce qui peut devenir compliqué quand on passe d'un OS à l'autre.
Cette fois ci en lançant l'appli vous aurez
Dans l'application elle même nous avons une console (comme dans Chrome). Via le menu, dans view->Toggle Developer Tools ou avec les raccourcis (alt cmd I pour Mac) et (Ctrl Maj I pour windows). Cependant, attention cette console ne prend en compte que le front car le processus de la fenêtre et celui de l'application son isolés (n'oubliez pas!).
Afin de pouvoir donner la longueur de la vidéo nous allons mettre en place un formulaire dans le html. La balise <input type="file" accept="video/*"/>
va utiliser les fonctionnalités du navigateur. Nous avons dit plus haut que nous ne pourrions pas accéder à l'OS directement. Ce n'est pas tout à fait vrai. Si nous utilisons les fonctionnalités du navigateur, pour le moment tout se passe comme avec une page web.
<!--index.html-->
<head>
</head>
<body>
<h1>Durée des Vidéos</h1>
<form>
<div>
<label for="fichier">Votre vidéo</label>
<input type="file" accept="video/*" id="fichier"/>
</div>
</form>
</body>
Si vous lancez l'application, vous verrez le bouton "Choisir un fichier". C'est le bouton par défaut de Chromium. En cliquant l'utilisateur pourra selectionner uniquement un fichier de type video (accept="video/*"
).
Comme dans un navigateur, vous pouvez obtenir les informations classique sur le fichier.
Ajoutez d'abord un bouton submit
et un peu de javascript (nous n'en mettrons plus dans l'html, ici c'est pour faire simple)
<!--index.html-->
<head>
</head>
<body>
<h1>Durée des Vidéos</h1>
<form>
<div>
<label for="fichier">Votre vidéo</label>
<input type="file" accept="video/*" id="fichier"/>
</div>
<button type="submit">go</button>
</form>
<script>
document.querySelector('form').addEventListener('submit', (event) => {
event.preventDefault();
const file = document.querySelector("input").files[0];
console.log(file);
});
</script>
</body>
Cette fois, si vous choisissez un fichier et que vous cliquez sur go. Vous obtenez dans la console (cf debug) les informations que chromium a récupéré.
Nous allons utiliser FFMPEG pour traiter notre vidéo. Commencez donc par l'installer sur votre machine si vous ne l'avez pas déjà.
Nous allons également utiliser fluent-ffmpeg, une librairie npm qui facilite le lien en tre ffmpeg et nodejs. Pour l'ajouter à notre projet:
npm install --save fluent-ffmpeg
Par convention, et comme bonne pratique, nous allons séparer ce que nous mettons dans index.js
correspondra à ce qui est lié à electron et son fonctionnement. En gros, ce qui touche au système. D'un autre côté, dans index.html
il y aura ce qui pourrai faire partie d'une application web.
De ce fait, comment récupérer le fichier dans index.html
et ensuite l'exploiter dans index.js
?
Nous allons utiliser l'IPC (Inter Process Communication) pour faire cette opération.
Déjà, afin de respecter la documentation d'electron, nous allons séparer le javascript du html. Créez un fichier preload.js
:
// preload.js
// Quand le DOM est chargé
window.addEventListener('DOMContentLoaded', () => {
// fait ce que l'on faisait tout à l'heure
document.querySelector('form').addEventListener('submit', (event) => {
event.preventDefault();
const file = document.querySelector("input").files[0];
console.log(file);
});
})
et enlevez le <script>...</script>
d'index.html
.
Remarquez que nous sommes obligé d'ajouter un écouteur sur la fenêtre
window
afin de s'assurer que la page est bien chargée intégralement avent de pourvoir selectionner les éléments avecquerySelector
.
Enfin ajoutez une propriété à notre BrowserWindow
dans index.js
// index.js
const { app, BrowserWindow } = require('electron')
const path = require('path')
// fonction de création de fenêtres
// n'oubliez pas l'import de BrowserWindow
function createWindow() {
const win = new BrowserWindow({
webPreferences: {
// charge le fichier preload.js en utillisant l'api path
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
}
app.on('ready', () => {
console.log("app est pret !")
createWindow()
})
Ajoutons ipcRenderer à nos dépendances dans preload.js
et utilisons l'ipcRenderer pour envoyer le chemin de la vidéo à electron:
const { ipcRenderer } = require("electron");
window.addEventListener('DOMContentLoaded', () => {
document.querySelector("form").addEventListener("submit", (event) => {
event.preventDefault();
// on récupère le chemin
const { path } = document.querySelector("input").files[0];
// on envoie le chemin avec une signature avec ipcRenderer
ipcRenderer.send('video:submit', path)
});
})
Du côté electron (index.js
) on doit récupérer l'information avec ipMain:
//index.json
const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const ffmpeg = require("fluent-ffmpeg");
// fonction de création de fenêtres
// n'oubliez pas l'import de BrowserWindow
function createWindow() {
const win = new BrowserWindow({
webPreferences: {
// charge le fichier preload.js en utillisant l'api path
preload: path.join(__dirname, "preload.js"),
},
});
win.loadFile("index.html");
}
// ouverture de la fenêtre
app.on("ready", () => {
console.log("app est pret !");
createWindow();
});
// action à l'envoie du chemin à partir de la vue
ipcMain.on("video:submit", (event, path) => {
// utilisation de fluent-ffmpeg
ffmpeg.ffprobe(path, (err, metadata) => {
// affichons la durée dans la console
console.log("La durée du film est de ", metadata.format.duration, ' secondes');
});
});
ATTENTION: le
console.log
d'index.js
s'affichera dans le terminal système et pas dans la console de debug car c'est nodejs qui travail.
Envoyons ça à la vue:
const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const ffmpeg = require("fluent-ffmpeg");
let win // on déclare la fenêtre ici pour la récupérer en dehors de l'évenement "on ready"
// fonction de création de fenêtres
// n'oubliez pas l'import de BrowserWindow
function createWindow() {
// ici on en lève le const
win = new BrowserWindow({
webPreferences: {
// charge le fichier preload.js en utillisant l'api path
preload: path.join(__dirname, "preload.js"),
},
});
win.loadFile("index.html");
}
// ouverture de la fenêtre
app.on("ready", () => {
console.log("app est pret !");
createWindow();
});
// action à l'envoie du chemin à partir de la vue
ipcMain.on("video:submit", (event, path) => {
// utilisation de fluent-ffmpeg
ffmpeg.ffprobe(path, (err, metadata) => {
// on envoie la durée à la vue
win.webContents.send('video:metadata',metadata.format.duration);
});
});
notez bien que nous avons été contraint de déclarer
win
en dehors de la fonction fléchée car sinon elle n'est pas accessible plus loin.
Côté vue (index.html
et preload.js
):
... <!--index.html-->
</form>
<h2 id="result"></h2>
</body>
et
// preload.js
// ...
ipcRenderer.on("video:metadata", (event, duration) => {
document.querySelector(
"#result"
).innerHTML = `Le film dure ${duration} secondes`;
});
En résumé:
On envoie vers electron et node avec ipcRenderer.send()
, on récupère de l'autre côté avec ipcMain.on()
. On répond avec webContents.send()
et on récupère cette réponse dans la vue avec ipcRenderer.on()
. Chacuns de ces échanges doit avoir une signature afin de le retrouver.
la documentation electronjs ici
Les sources du cours ici