Avec la toute nouvelle API Media Session, vous pouvez désormais personnaliser les notifications multimédias en fournissant des métadonnées pour les contenus multimédias lus par votre application Web. Il vous permet également de gérer les événements liés aux contenus multimédias, tels que la recherche ou le changement de piste, qui peuvent provenir de notifications ou de touches multimédias. Ça vous plaît ? Essayez les exemples officiels de session multimédia.
L'API Media Session est compatible avec Chrome 57 (bêta en février 2017, stable en mars 2017).
Donne-moi ce que je veux
Vous connaissez déjà l'API Media Session et vous revenez simplement à copier-coller sans vergogne du code standard ? Voilà.
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
artist: 'Rick Astley',
album: 'Whenever You Need Somebody',
artwork: [
{ src: 'https://dummyimage.com/96x96', sizes: '96x96', type: 'image/png' },
{ src: 'https://dummyimage.com/128x128', sizes: '128x128', type: 'image/png' },
{ src: 'https://dummyimage.com/192x192', sizes: '192x192', type: 'image/png' },
{ src: 'https://dummyimage.com/256x256', sizes: '256x256', type: 'image/png' },
{ src: 'https://dummyimage.com/384x384', sizes: '384x384', type: 'image/png' },
{ src: 'https://dummyimage.com/512x512', sizes: '512x512', type: 'image/png' },
]
});
navigator.mediaSession.setActionHandler('play', function() {});
navigator.mediaSession.setActionHandler('pause', function() {});
navigator.mediaSession.setActionHandler('seekbackward', function() {});
navigator.mediaSession.setActionHandler('seekforward', function() {});
navigator.mediaSession.setActionHandler('previoustrack', function() {});
navigator.mediaSession.setActionHandler('nexttrack', function() {});
}
Se plonger dans le code
Jouons 🎷
Ajoutez un élément <audio>
simple à votre page Web et attribuez plusieurs sources multimédias afin que le navigateur puisse choisir celle qui fonctionne le mieux.
<audio controls>
<source src="audio.mp3" type="audio/mp3"/>
<source src="audio.ogg" type="audio/ogg"/>
</audio>
Comme vous le savez peut-être, autoplay
est désactivé pour les éléments audio sur Chrome pour Android, ce qui signifie que nous devons utiliser la méthode play()
de l'élément audio. Cette méthode doit être déclenchée par un geste utilisateur, tel qu'une pression ou un clic de souris.
Cela signifie écouter les événements pointerup
, click
et touchend
. En d'autres termes, l'utilisateur doit cliquer sur un bouton avant que votre application Web puisse émettre du son.
playButton.addEventListener('pointerup', function(event) {
let audio = document.querySelector('audio');
// User interacted with the page. Let's play audio...
audio.play()
.then(_ => { /* Set up media session... */ })
.catch(error => { console.log(error) });
});
Si vous ne souhaitez pas lire de contenu audio juste après la première interaction, nous vous recommandons d'utiliser la méthode load()
de l'élément audio. C'est l'un des moyens pour le navigateur de savoir si l'utilisateur a interagi avec l'élément. Notez que cela peut également contribuer à fluidifier la lecture, car le contenu sera déjà chargé.
let audio = document.querySelector('audio');
welcomeButton.addEventListener('pointerup', function(event) {
// User interacted with the page. Let's load audio...
<strong>audio.load()</strong>
.then(_ => { /* Show play button for instance... */ })
.catch(error => { console.log(error) });
});
// Later...
playButton.addEventListener('pointerup', function(event) {
<strong>audio.play()</strong>
.then(_ => { /* Set up media session... */ })
.catch(error => { console.log(error) });
});
Personnaliser la notification
Lorsque votre application Web lit du contenu audio, une notification multimédia s'affiche déjà dans la barre de notification. Sur Android, Chrome s'efforce de présenter les informations appropriées à l'aide du titre du document et de l'image d'icône la plus grande qu'il puisse trouver.
Définir les métadonnées
Voyons comment personnaliser cette notification multimédia en définissant des métadonnées de session multimédia telles que le titre, l'artiste, le nom de l'album et l'illustration avec l'API Media Session.
// When audio starts playing...
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
artist: 'Rick Astley',
album: 'Whenever You Need Somebody',
artwork: [
{ src: 'https://dummyimage.com/96x96', sizes: '96x96', type: 'image/png' },
{ src: 'https://dummyimage.com/128x128', sizes: '128x128', type: 'image/png' },
{ src: 'https://dummyimage.com/192x192', sizes: '192x192', type: 'image/png' },
{ src: 'https://dummyimage.com/256x256', sizes: '256x256', type: 'image/png' },
{ src: 'https://dummyimage.com/384x384', sizes: '384x384', type: 'image/png' },
{ src: 'https://dummyimage.com/512x512', sizes: '512x512', type: 'image/png' },
]
});
}
Une fois la lecture terminée, vous n'avez pas besoin de "libérer" la session multimédia, car la notification disparaît automatiquement. N'oubliez pas que l'navigator.mediaSession.metadata
actuelle sera utilisée au début de la lecture. C'est pourquoi vous devez le mettre à jour pour vous assurer de toujours afficher des informations pertinentes dans la notification multimédia.
Titre précédent / suivant
Si votre application Web fournit une playlist, vous pouvez autoriser l'utilisateur à parcourir votre playlist directement à partir de la notification multimédia à l'aide d'icônes "Piste précédente" et "Piste suivante".
let audio = document.createElement('audio');
let playlist = ['audio1.mp3', 'audio2.mp3', 'audio3.mp3'];
let index = 0;
navigator.mediaSession.setActionHandler('previoustrack', function() {
// User clicked "Previous Track" media notification icon.
index = (index - 1 + playlist.length) % playlist.length;
playAudio();
});
navigator.mediaSession.setActionHandler('nexttrack', function() {
// User clicked "Next Track" media notification icon.
index = (index + 1) % playlist.length;
playAudio();
});
function playAudio() {
audio.src = playlist[index];
audio.play()
.then(_ => { /* Set up media session... */ })
.catch(error => { console.log(error); });
}
playButton.addEventListener('pointerup', function(event) {
playAudio();
});
Notez que les gestionnaires d'actions multimédias seront conservés. Ce modèle est très semblable au modèle d'écouteur d'événements, sauf que la gestion d'un événement signifie que le navigateur cesse d'exécuter tout comportement par défaut et l'utilise comme signal indiquant que votre application Web est compatible avec l'action multimédia. Par conséquent, les commandes d'action multimédias ne s'affichent que si vous définissez le gestionnaire d'action approprié.
Par ailleurs, désactiver un gestionnaire d'action multimédia est aussi simple que de l'attribuer à null
.
Revenir en arrière / Avancer
L'API Media Session vous permet d'afficher les icônes de notification multimédia "Rechercher en arrière" et "Rechercher en avant" si vous souhaitez contrôler la durée de la lecture à sauter.
let skipTime = 10; // Time to skip in seconds
navigator.mediaSession.setActionHandler('seekbackward', function() {
// User clicked "Seek Backward" media notification icon.
audio.currentTime = Math.max(audio.currentTime - skipTime, 0);
});
navigator.mediaSession.setActionHandler('seekforward', function() {
// User clicked "Seek Forward" media notification icon.
audio.currentTime = Math.min(audio.currentTime + skipTime, audio.duration);
});
Lecture / Pause
L'icône "Lire/Mettre en pause" s'affiche toujours dans la notification multimédia, et les événements associés sont gérés automatiquement par le navigateur. Si, pour une raison quelconque, le comportement par défaut ne fonctionne pas, vous pouvez toujours gérer les événements multimédias "Play" (Lecture) et "Pause" (Pause).
navigator.mediaSession.setActionHandler('play', function() {
// User clicked "Play" media notification icon.
// Do something more than just playing current audio...
});
navigator.mediaSession.setActionHandler('pause', function() {
// User clicked "Pause" media notification icon.
// Do something more than just pausing current audio...
});
Notifications omniprésentes
L'avantage de l'API Media Session est que la barre de notification n'est pas le seul endroit où les métadonnées et les commandes multimédias sont visibles. La notification multimédia est synchronisée automatiquement avec tous les appareils connectés. Il s'affiche également sur les écrans de verrouillage.
Rendre l'application compatible avec la lecture hors connexion
Je sais ce que vous pensez. Service worker à la rescousse !
C'est vrai, mais avant tout, vous devez vous assurer que tous les éléments de cette checklist sont cochés:
- Tous les fichiers multimédias et d'illustrations sont diffusés avec l'en-tête HTTP
Cache-Control
approprié. Cela permet au navigateur de mettre en cache et de réutiliser les ressources précédemment récupérées. Consultez la checklist de mise en cache. - Assurez-vous que tous les fichiers multimédias et d'illustrations sont diffusés avec l'en-tête HTTP
Allow-Control-Allow-Origin: *
. Cela permettra aux applications Web tierces de récupérer et de consommer des réponses HTTP à partir de votre serveur Web.
Stratégie de mise en cache du service worker
Pour les fichiers multimédias, je recommande une stratégie simple de mise en cache, avec recours au réseau, comme illustré par Jake Archibald.
Pour les illustrations, je serais un peu plus précis et choisirais l'approche ci-dessous:
- L'illustration
If
est déjà dans le cache. Diffuser-la à partir du cache Else
récupérer des illustrations à partir du réseau- La récupération
If
est réussie. Ajoutez l'illustration du réseau au cache et affichez-la. Else
Diffuser l'illustration de remplacement à partir du cache
- La récupération
De cette manière, les notifications multimédias auront toujours une belle icône d'illustration, même lorsque le navigateur ne peut pas les récupérer. Pour ce faire, procédez comme suit:
const FALLBACK_ARTWORK_URL = 'fallbackArtwork.png';
addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(initArtworkCache());
});
function initArtworkCache() {
caches.open('artwork-cache-v1')
.then(cache => cache.add(FALLBACK_ARTWORK_URL));
}
addEventListener('fetch', event => {
if (/artwork-[0-9]+\.png$/.test(event.request.url)) {
event.respondWith(handleFetchArtwork(event.request));
}
});
function handleFetchArtwork(request) {
// Return cache request if it's in the cache already, otherwise fetch
// network artwork.
return getCacheArtwork(request)
.then(cacheResponse => cacheResponse || getNetworkArtwork(request));
}
function getCacheArtwork(request) {
return caches.open('artwork-cache-v1')
.then(cache => cache.match(request));
}
function getNetworkArtwork(request) {
// Fetch network artwork.
return fetch(request)
.then(networkResponse => {
if (networkResponse.status !== 200) {
return Promise.reject('Network artwork response is not valid');
}
// Add artwork to the cache for later use and return network response.
addArtworkToCache(request, networkResponse.clone())
return networkResponse;
})
.catch(error => {
// Return cached fallback artwork.
return getCacheArtwork(new Request(FALLBACK_ARTWORK_URL))
});
}
function addArtworkToCache(request, response) {
return caches.open('artwork-cache-v1')
.then(cache => cache.put(request, response));
}
Autoriser l'utilisateur à contrôler le cache
À mesure que l'utilisateur consomme du contenu à partir de votre application Web, les fichiers multimédias et les illustrations peuvent prendre beaucoup d'espace sur son appareil. Il est de votre responsabilité de montrer la quantité de cache utilisée et de permettre aux utilisateurs de l'effacer. Heureusement pour nous, cela est assez facile avec l'API Cache.
// Here's how I'd compute how much cache is used by artwork files...
caches.open('artwork-cache-v1')
.then(cache => cache.matchAll())
.then(responses => {
let cacheSize = 0;
let blobQueue = Promise.resolve();
responses.forEach(response => {
let responseSize = response.headers.get('content-length');
if (responseSize) {
// Use content-length HTTP header when possible.
cacheSize += Number(responseSize);
} else {
// Otherwise, use the uncompressed blob size.
blobQueue = blobQueue.then(_ => response.blob())
.then(blob => { cacheSize += blob.size; blob.close(); });
}
});
return blobQueue.then(_ => {
console.log('Artwork cache is about ' + cacheSize + ' Bytes.');
});
})
.catch(error => { console.log(error); });
// And here's how to delete some artwork files...
const artworkFilesToDelete = ['artwork1.png', 'artwork2.png', 'artwork3.png'];
caches.open('artwork-cache-v1')
.then(cache => Promise.all(artworkFilesToDelete.map(artwork => cache.delete(artwork))))
.catch(error => { console.log(error); });
Notes de mise en œuvre
- Chrome pour Android demande une attention audio "complète" pour afficher les notifications multimédias uniquement lorsque la durée du fichier multimédia est d'au moins cinq secondes.
- Les illustrations de notification acceptent les URL de blob et les URL de données.
- Si aucune illustration n'est définie et qu'une image d'icône est disponible dans la taille souhaitée, les notifications multimédias l'utiliseront.
- La taille de l'illustration de notification dans Chrome pour Android est
512x512
. Pour les appareils bas de gamme, il s'agit de256x256
. - Ignorez les notifications multimédias avec
audio.src = ''
. - Étant donné que l'API Web Audio ne demande pas la mise au point audio Android pour des raisons historiques, le seul moyen de la faire fonctionner avec l'API Media Session consiste à connecter un élément
<audio>
en tant que source d'entrée à l'API Web Audio. Nous espérons que l'API Web AudioFocus proposée améliorera la situation dans un avenir proche. - Les appels de session multimédia n'affectent les notifications multimédias que s'ils proviennent du même frame que la ressource multimédia. Consultez l'extrait ci-dessous.
<iframe id="iframe">
<audio>...</audio>
</iframe>
<script>
iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
...
});
</script>
Assistance
Au moment de la rédaction de cet article, Chrome pour Android est la seule plate-forme compatible avec l'API Media Session. Pour obtenir des informations à jour sur l'état de l'implémentation du navigateur, consultez État de la plate-forme Chrome.
Exemples et démonstrations
Consultez nos exemples de session multimédia officiels pour Chrome, qui incluent la Blender Foundation et le travail de Jan Morgenstern.
Ressources
Spécification de la session multimédia : wicg.github.io/mediasession
Problèmes de spécification : github.com/WICG/mediasession/issues
Bugs Chrome : crbug.com