Con la nuova API Media Session, ora puoi personalizzare le notifiche dei contenuti multimediali fornendo i metadati dei contenuti multimediali riprodotti dalla tua app web. Ti consente inoltre di gestire gli eventi relativi ai contenuti multimediali, come la ricerca o la modifica dei canali, che possono provenire da notifiche o tasti multimediali. Interessante, no? Prova gli esempi di sessioni multimediali ufficiali.
L'API Media Session è supportata in Chrome 57 (beta a febbraio 2017, stabile a marzo 2017).
Dammi quello che voglio
Conosci già l'API Media Session e vuoi semplicemente copiare e incollare senza problemi del codice boilerplate? Eccolo qui.
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() {});
}
Esplorare il codice
Giochiamo 🎷
Aggiungi un semplice elemento <audio>
alla tua pagina web e assegna più origini multimediali in modo che il browser possa scegliere quella più adatta.
<audio controls>
<source src="audio.mp3" type="audio/mp3"/>
<source src="audio.ogg" type="audio/ogg"/>
</audio>
Come forse saprai, autoplay
è disattivato per gli elementi audio su Chrome per Android, il che significa che dobbiamo utilizzare 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 possa effettivamente emettere un suono.
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 per il browser di tenere traccia dell'interazione dell'utente con l'elemento. Tieni presente che questa operazione potrebbe anche contribuire a rendere più fluida la riproduzione, perché 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) });
});
Personalizzare la notifica
Quando la tua app web riproduce audio, puoi già vedere una notifica multimediale nella barra delle notifiche. Su Android, Chrome fa del suo meglio per mostrare informazioni appropriate utilizzando il titolo del documento e l'immagine icona più grande che riesce a trovare.
Impostare i metadati
Vediamo come personalizzare questa notifica multimediale impostando alcuni metadati della sessione multimediale, come titolo, artista, nome dell'album e 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 devi "rilasciare" la sessione multimediale perché la notifica scompare automaticamente. Tieni presente che verrà utilizzato il valore corrente di navigator.mediaSession.metadata
all'avvio di qualsiasi riproduzione. Per questo motivo, devi aggiornarlo per assicurarti di mostrare sempre informazioni pertinenti nella notifica multimediale.
Traccia precedente / successiva
Se la tua app web fornisce una playlist, ti consigliamo di consentire all'utente di navigare tra i brani direttamente dalla notifica multimediale con alcune icone "Brano precedente" e "Brano successivo".
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 rimarranno invariati. È molto simile al pattern di ascoltatori di eventi, tranne per il fatto che la gestione di un evento comporta l'interruzione del comportamento predefinito del browser e viene utilizzato come indicatore del fatto che la tua app web supporta l'azione multimediale. Pertanto, i controlli delle azioni multimediali non verranno visualizzati a meno che non imposti il gestore delle azioni appropriato.
A proposito, è facile annullare l'impostazione di un gestore di azioni multimediali come assegnarlo a null
.
Andare indietro / avanti
L'API Media Session ti consente di mostrare le icone di notifica multimediale "Avanti veloce" e "Indietro veloce" 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);
});
Riproduci / Metti in pausa
L'icona "Riproduci/Metti in pausa" viene sempre visualizzata nella notifica multimediale 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
La cosa interessante dell'API Media Session è che la barra delle notifiche non è l'unico posto in cui sono visibili i metadati e i controlli multimediali. La notifica relativa ai contenuti multimediali viene sincronizzata automaticamente con qualsiasi dispositivo indossabile accoppiato. Inoltre, viene visualizzato anche nelle schermate di blocco.
Riproduci offline
So cosa stai pensando. Service worker al salvataggio!
È vero, ma prima di tutto devi assicurarti che tutti gli elementi di questo elenco di controllo siano selezionati:
- Tutti i file di annunci e artwork vengono pubblicati con l'intestazione HTTP
Cache-Control
appropriata. In questo modo il browser potrà memorizzare nella cache e riutilizzare le risorse recuperate in precedenza. Consulta l'elenco di controllo relativo alla memorizzazione nella cache. - Assicurati che tutti i file multimediali e di artwork vengano pubblicati con l'intestazione HTTP
Allow-Control-Allow-Origin: *
. In questo modo, le app web di terze parti potranno 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, consiglio una semplice strategia "Cache, con fallback alla rete" come illustrato da Jake Archibald.
Per l'artwork, però, sarei un po' più specifico e sceglierei l'approccio riportato di seguito:
- L'artwork di
If
è già nella cache, pubblicala dalla cache Else
recupera l'artwork dalla rete- Se il recupero di
If
è andato a buon fine, aggiungi l'artwork della rete alla cache e pubblicala Else
pubblica l'artwork di riserva dalla cache
- Se il recupero di
In questo modo, le notifiche multimediali avranno sempre un'icona dell'artwork anche quando il browser non riesce a recuperarle. Ecco come puoi 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
Quando l'utente utilizza i contenuti della tua app web, i file multimediali e di artwork possono occupare molto spazio sul suo dispositivo. È tua responsabilità mostrare quanta cache viene utilizzata e dare agli utenti la possibilità di cancellarla. Fortunatamente, eseguire questa operazione è abbastanza 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 l'attenzione audio "completa" per mostrare le notifiche multimediali solo quando la durata del file multimediale è di almeno 5 secondi.
- L'artwork delle notifiche supporta gli URL blob e gli URL di dati.
- Se non è definito alcun artwork e esiste un'immagine icona con dimensioni adeguate, le notifiche multimediali la utilizzeranno.
- Le dimensioni dell'artwork delle notifiche in Chrome per Android sono
512x512
. Per i dispositivi di fascia bassa, è256x256
. - Ignora le notifiche multimediali con
audio.src = ''
. - Poiché l'API Web Audio non richiede l'attenzione audio di Android per motivi storici, l'unico modo per farlo funzionare con l'API Media Session è collegare un elemento
<audio>
come sorgente di input all'API Web Audio. Ci auguriamo che la proposta di API Web AudioFocus migliori la situazione nel prossimo futuro. - Le chiamate Media Session influiscono sulle notifiche multimediali solo se provengono dallo stesso frame della risorsa multimediale. Guarda 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 della stesura del presente documento, Chrome per Android è l'unica piattaforma che supporta l'API Media Session. Puoi trovare informazioni più aggiornate sullo stato dell'implementazione del browser nella pagina Stato della piattaforma Chrome.
Samples e demo
Dai un'occhiata ai nostri Sample di sessioni multimediali ufficiali di Chrome con 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