Personalizza le notifiche per i contenuti multimediali e gestisci le playlist

François Beaufort
François Beaufort

Con la nuovissima API Media Session, ora puoi personalizzare le notifiche multimediali fornendo i metadati per i contenuti multimediali in riproduzione nella tua app web. Consente inoltre di gestire eventi correlati ai contenuti multimediali, come la ricerca o la modifica del canale, che possono provenire dalle notifiche o dai tasti multimediali. Interessante, no? Prova gli esempi di sessioni multimediali ufficiali.

L'API Media Session è supportata in Chrome 57 (beta a febbraio 2017, versione stabile a marzo 2017).

TL;DR della sessione multimediale;
Foto di Michael Alø-Nielsen / CC BY 2.0

Dammi quello che voglio

Conosci già l'API Media Session e vuoi semplicemente tornare a copiare e incollare un codice boilerplate? Eccolo.

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

Scopri il codice

Giochiamo 🎷

Aggiungi un semplice elemento <audio> alla tua pagina web e assegna diverse origini multimediali in modo che il browser possa scegliere quale funziona meglio.

<audio controls>
    <source src="audio.mp3" type="audio/mp3"/>
    <source src="audio.ogg" type="audio/ogg"/>
</audio>

Come forse saprai, l'elemento autoplay è disattivato per gli elementi audio su Chrome per Android, il che significa che dobbiamo usare il metodo play() dell'elemento audio. Questo metodo deve essere attivato da un gesto dell'utente, ad esempio un tocco o un clic del mouse. Ciò significa ascoltare gli eventi pointerup, click e touchend. In altre parole, l'utente deve fare clic su un pulsante prima che la tua app web produca effettivamente rumore.

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

Se non vuoi riprodurre l'audio subito dopo la prima interazione, ti consiglio di utilizzare il metodo load() dell'elemento audio. Questo è un modo in cui il browser può monitorare se l'utente ha interagito con l'elemento. Tieni presente che questa operazione potrebbe anche rendere più fluida la riproduzione, in quanto i contenuti saranno già caricati.

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

Personalizza la notifica

Quando la tua app web riproduce contenuti audio, puoi già vedere una notifica dei contenuti multimediali nella barra delle notifiche. Su Android, Chrome fa del suo meglio per mostrare le informazioni appropriate utilizzando il titolo del documento e l'immagine dell'icona più grande che riesce a trovare.

Senza sessione multimediale
Senza sessione multimediale
Con sessione multimediale
Con sessione multimediale

Imposta metadati

Vediamo come personalizzare questa notifica multimediale impostando alcuni metadati della sessione multimediale, come il titolo, l'artista, il nome dell'album e l'artwork con 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' },
    ]
    });
}

Al termine della riproduzione non è necessario "rilasciare" la sessione multimediale, in quanto la notifica scomparirà automaticamente. Tieni presente che all'avvio di qualsiasi riproduzione verrà usata l'opzione navigator.mediaSession.metadata corrente. Per questo motivo, devi aggiornarla per assicurarti di mostrare sempre informazioni pertinenti nella notifica dei contenuti multimediali.

Traccia precedente / traccia successiva

Se la tua applicazione web fornisce una playlist, puoi consentire all'utente di esplorare la playlist direttamente dalla notifica multimediale con alcune icone "Traccia precedente" e "Traccia successiva".

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

Tieni presente che i gestori delle azioni multimediali continueranno a esistere. Questo è molto simile al pattern di ascolto degli eventi, tranne per il fatto che, se gestisci un evento, il browser smette di eseguire qualsiasi comportamento predefinito e lo utilizza come segnale che la tua app web supporta l'azione multimediale. Di conseguenza, i controlli delle azioni multimediali non verranno mostrati a meno che non imposti il gestore delle azioni appropriato.

Per inciso, annullare l'impostazione di un gestore delle azioni multimediali è semplice quanto assegnarlo a null.

Vai indietro / Vai avanti

L'API Media Session ti consente di mostrare le icone di notifica contenuti multimediali "Vai indietro" e "Vai avanti" se vuoi controllare la quantità di tempo saltata.

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

Riprodurre / mettere in pausa

L'icona "Riproduci/Metti in pausa" viene sempre visualizzata nella notifica dei contenuti multimediali e gli eventi correlati vengono gestiti automaticamente dal browser. Se per qualche motivo il comportamento predefinito non funziona, puoi comunque gestire gli eventi multimediali "Riproduci" e "Metti in pausa".

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

Notifiche ovunque

L'aspetto interessante dell'API Media Session è che la barra delle notifiche non è l'unico punto in cui sono visibili i metadati e i controlli dei contenuti multimediali. La notifica dei contenuti multimediali viene sincronizzata automaticamente per magia con qualsiasi dispositivo indossabile accoppiato. ed è visibile anche nelle schermate di blocco.

Schermata di blocco
Blocco schermo - Foto di Michael Alø-Nielsen/ CC BY 2.0
Notifica Wear
Notifica di utilizzo

Riproduci i contenuti offline

So cosa stai pensando adesso. Il Service worker ti viene in soccorso!

Vero, ma prima di tutto devi assicurarti che tutti gli elementi di questo elenco di controllo siano stati verificati:

  • Tutti i file multimediali e gli artwork vengono pubblicati con l'intestazione HTTP Cache-Control appropriata. Ciò consentirà al browser di memorizzare nella cache e riutilizzare le risorse recuperate in precedenza. Consulta l'Elenco di controllo per la memorizzazione nella cache.
  • Assicurati che tutti i file multimediali e gli artwork vengano pubblicati con l'intestazione HTTP Allow-Control-Allow-Origin: *. Ciò consentirà alle app web di terze parti di recuperare e utilizzare le risposte HTTP dal tuo server web.

La strategia di memorizzazione nella cache del service worker

Per quanto riguarda i file multimediali, ti consiglio una semplice strategia "Cache, fallback di rete", come illustrato da Jake Archibald.

Per gli artwork, però, sarei un po' più specifico e scelgo l'approccio qui sotto:

  • L'artwork If è già nella cache, pubblicalo dalla cache
  • Else recupera l'artwork dalla rete
    • Il recupero di If è riuscito, aggiungi l'artwork di rete alla cache e pubblicalo
    • Else pubblica l'artwork di riserva dalla cache

In questo modo, le notifiche multimediali avranno sempre una bella icona grafica anche quando il browser non riesce a recuperarle. Ecco come potresti implementare questa funzionalità:

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

Consenti all'utente di controllare la cache

Poiché l'utente utilizza i contenuti della tua app web, i file multimediali e degli artwork potrebbero occupare molto spazio sul dispositivo. È tua responsabilità mostrare la quantità di cache utilizzata e offrire agli utenti la possibilità di svuotarla. Fortunatamente per noi, farlo è piuttosto semplice con 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); });

Note sull'implementazione

  • Chrome per Android richiede lo stato attivo dell'audio "completo" per mostrare le notifiche multimediali solo quando la durata del file multimediale è almeno 5 secondi.
  • L'artwork delle notifiche supporta URL BLOB e URL di dati.
  • Se non viene definito alcun artwork e viene utilizzata un'immagine di un'icona di dimensioni adeguate, verrà utilizzata per le notifiche multimediali.
  • Le dimensioni dell'artwork delle notifiche in Chrome per Android sono 512x512. Per i dispositivi di fascia bassa, il valore è 256x256.
  • Ignora le notifiche di contenuti multimediali con audio.src = ''.
  • Poiché l'API Web Audio non richiede Android Audio Focus per motivi storici, l'unico modo per farlo funzionare con l'API Media Session è collegare un elemento <audio> come origine di input all'API Web Audio. Speriamo che l'API Web AudioFocus proposta migliorerà la situazione nel prossimo futuro.
  • Le chiamate della sessione multimediale influiranno sulle notifiche relative ai contenuti multimediali solo se provengono dallo stesso frame della risorsa multimediale. Vedi lo snippet di seguito.
<iframe id="iframe">
  <audio>...</audio>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Assistenza

Al momento, Chrome per Android è l'unica piattaforma che supporta l'API Media Session. Informazioni più aggiornate sullo stato dell'implementazione del browser sono disponibili nella pagina Stato della piattaforma Chrome.

Esempi e demo

Dai un'occhiata ai nostri esempi di sessioni multimediali di Chrome ufficiali che includono Blender Foundation e il lavoro di Jan Morgenstern.

Risorse

Specifiche della sessione multimediale: wicg.github.io/mediasession

Problemi relativi alle specifiche: github.com/WICG/mediasession/issues

Bug di Chrome: crbug.com