Personnaliser les notifications multimédias et gérer les playlists

François Beaufort
François Beaufort

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 multimédias tels que la recherche ou les modifications de suivi, qui peuvent provenir de notifications ou de touches multimédias. Ça vous plaît ? Essayez les exemples officiels de sessions multimédias.

L'API Media Session est compatible avec Chrome 57 (bêta en février 2017, stable en mars 2017).

Résumé de la session média ;
Photo par Michael Alø-Nielsen / CC BY 2.0

Donne ce que je veux

Vous connaissez déjà l'API Media Session, et vous souhaitez simplement copier-coller du code récurrent sans honte ? 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() {});
}

Saisissez 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 de l'utilisateur, comme une pression ou un clic de souris. Cela implique d'écouter les événements pointerup, click et touchend. En d'autres termes, l'utilisateur doit cliquer sur un bouton pour que votre application Web puisse émettre du bruit.

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. Cela permet au navigateur de savoir si l'utilisateur a interagi avec l'élément. Notez que cela peut également faciliter 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 d'afficher les informations appropriées en se basant sur le titre du document et la plus grande image d'icône disponible.

Sans session multimédia
Sans session multimédia
Avec session multimédia
Avec une session multimédia

Définir les métadonnées

Voyons comment personnaliser cette notification multimédia en définissant certaines métadonnées de session multimédia telles que le titre, l'artiste, le nom de l'album et la pochette 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 la valeur 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 des icônes "Titre précédent" 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, à la différence que la gestion d'un événement signifie que le navigateur cesse d'effectuer tout comportement par défaut et l'utilise comme signal indiquant que votre application Web prend en charge l'action multimédia. Par conséquent, les commandes des actions multimédias ne s'affichent que si vous définissez le gestionnaire d'actions approprié.

À ce propos, il est aussi facile de désactiver un gestionnaire d'actions multimédias que de l'attribuer à null.

Avancer / Reculer

L'API Media Session vous permet d'afficher les icônes de notification multimédia "Avancer" et "Avancer" si vous souhaitez contrôler la durée d'inactivité.

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 Lecture/Pause est toujours affichée 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 "Lecture" et "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 partout

L'avantage de l'API Media Session est que la barre de notification n'est pas le seul emplacement où les métadonnées et les commandes multimédias sont visibles. La notification multimédia est synchronisée automatiquement avec tout accessoire connecté associé. Et elle apparaît aussi sur les écrans de verrouillage.

Verrouiller l&#39;écran
Verrouillage d'écran - Photo par Michael Alø-Nielsen / CC BY 2.0
Notification Wear
Notification Wear

Profitez d'une qualité de lecture hors connexion

Je sais ce que vous pensez maintenant. Service worker à la rescousse !

Vrai, mais avant tout, vous devez vous assurer que tous les éléments de cette checklist sont vérifiés:

  • Tous les fichiers multimédias et d'illustration sont diffusés avec l'en-tête HTTP Cache-Control approprié. Cela permettra 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'illustration sont diffusés avec l'en-tête HTTP Allow-Control-Allow-Origin: *. Cela permettra aux applications Web tierces de récupérer et d'utiliser les réponses HTTP de votre serveur Web.

La stratégie de mise en cache d'un service worker

Concernant les fichiers multimédias, je recommande une stratégie simple "Cache, falling to network" (Mettre en cache, revenir au réseau), comme l'illustre Jake Archibal.

Pour les illustrations, je serais un peu plus précis et choisirais l'approche ci-dessous:

  • L'illustration If se trouve déjà dans le cache. Diffusez-la à partir du cache.
  • Else récupère une illustration sur le réseau
    • La récupération de If a réussi. Ajoutez l'illustration de réseau au cache et diffusez-la.
    • Else diffuse l'illustration de remplacement à partir du cache.

Ainsi, les notifications multimédias auront toujours une belle icône d'illustration même lorsque le navigateur ne peut pas les récupérer. Voici comment vous pourriez procéder:

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

Comme l'utilisateur utilise le contenu de votre application Web, les fichiers multimédias et d'illustration peuvent prendre beaucoup d'espace sur son appareil. Il vous incombe d'indiquer la quantité de cache utilisée et de permettre aux utilisateurs de le vider. 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 le ciblage audio "complet" pour n'afficher les notifications multimédias que si le fichier multimédia dure au moins cinq secondes.
  • Les illustrations de notification acceptent les URL blob et les URL de données.
  • Si aucune illustration n'est définie et qu'il existe une image d'icône à une taille souhaitée, les notifications multimédias l'utiliseront.
  • La taille de l'illustration des notifications dans Chrome pour Android est de 512x512. Pour les appareils d'entrée de gamme, il s'agit de 256x256.
  • Ignorer les notifications multimédias avec audio.src = ''.
  • Étant donné que l'API Web Audio ne demande pas la priorité 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 permettra d'améliorer 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 ce document, Chrome pour Android est la seule plate-forme compatible avec l'API Media Session. Pour obtenir des informations plus récentes sur l'état de la mise en œuvre du navigateur, consultez État de la plate-forme Chrome.

Exemples et démonstrations

Consultez nos exemples officiels de sessions multimédias Chrome, dans lesquelles la Blender Foundation et le travail de Jan Morgenstern sont présentés.

Ressources

Spécification de la session multimédia : wicg.github.io/mediasession

Problèmes de spécifications : github.com/WICG/mediasession/issues

Bugs Chrome : crbug.com