Pas mediameldingen aan en beheer afspeellijsten

François Beaufort
François Beaufort

Met de gloednieuwe Media Session API kunt u nu mediameldingen aanpassen door metagegevens op te geven voor de media die uw webapp afspeelt. Hiermee kunt u ook mediagerelateerde gebeurtenissen afhandelen, zoals het zoeken naar of volgen van wijzigingen die afkomstig kunnen zijn van meldingen of mediatoetsen. Opgewonden? Probeer de officiële Media Session-voorbeelden .

De Media Session API wordt ondersteund in Chrome 57 (bèta in februari 2017, stabiel in maart 2017).

Mediasessie TL;DR;
Foto door Michael Alø-Nielsen / CC BY 2.0

Geef me wat ik wil

U kent de Media Session API al en komt gewoon terug om zonder enige schaamte wat standaardcode te kopiëren en plakken? Dus hier is het.

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

Verdiep je in de code

Laten we spelen 🎷

Voeg een eenvoudig <audio> -element toe aan uw webpagina en wijs meerdere mediabronnen toe, zodat de browser kan kiezen welke het beste werkt.

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

Zoals u wellicht weet, is autoplay uitgeschakeld voor audio-elementen in Chrome voor Android, wat betekent dat we de play() -methode van het audio-element moeten gebruiken. Deze methode moet worden geactiveerd door een gebruikersgebaar, zoals een aanraking of een muisklik. Dat betekent luisteren naar pointerup , click en touchend -gebeurtenissen. Met andere woorden: de gebruiker moet op een knop klikken voordat uw webapp daadwerkelijk geluid kan maken.

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

Als je niet direct na de eerste interactie audio wilt afspelen, raad ik je aan de methode load() van het audio-element te gebruiken. Dit is een manier voor de browser om bij te houden of de gebruiker interactie heeft gehad met het element. Houd er rekening mee dat dit ook kan helpen het afspelen soepeler te laten verlopen, omdat de inhoud al is geladen.

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

Pas de melding aan

Wanneer uw webapp audio afspeelt, ziet u al een mediamelding in het meldingenvak. Op Android doet Chrome zijn best om de juiste informatie weer te geven door de titel van het document en de grootste pictogramafbeelding te gebruiken die het kan vinden.

Zonder mediasessie
Zonder mediasessie
Met mediasessie
Met mediasessie

Metagegevens instellen

Laten we eens kijken hoe we deze mediamelding kunnen aanpassen door enkele metagegevens van de mediasessie in te stellen, zoals de titel, artiest, albumnaam en illustraties met de Media Session API.

// 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' },
    ]
    });
}

Zodra het afspelen is voltooid, hoeft u de mediasessie niet meer te "vrijgeven", aangezien de melding automatisch verdwijnt. Houd er rekening mee dat de huidige navigator.mediaSession.metadata zal worden gebruikt wanneer het afspelen begint. Daarom moet u deze updaten om ervoor te zorgen dat u altijd relevante informatie weergeeft in de mediamelding.

Vorig nummer/volgend nummer

Als uw webapp een afspeellijst biedt, wilt u de gebruiker mogelijk rechtstreeks vanuit de mediamelding door uw afspeellijst laten navigeren met enkele pictogrammen voor 'Vorige track' en 'Volgende track'.

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

Houd er rekening mee dat media-actiehandlers blijven bestaan. Dit lijkt sterk op het gebeurtenislistenerpatroon, behalve dat het afhandelen van een gebeurtenis betekent dat de browser geen standaardgedrag meer uitvoert en dit gebruikt als signaal dat uw web-app de mediaactie ondersteunt. Daarom worden de bedieningselementen voor media-acties alleen weergegeven als u de juiste actie-handler instelt.

Overigens is het uitschakelen van een media-actiehandler net zo eenvoudig als het toewijzen ervan aan null .

Zoek achteruit / zoek vooruit

Met de Media Session API kunt u de mediameldingspictogrammen "Zoek achteruit" en "Zoek vooruit" weergeven als u de hoeveelheid overgeslagen tijd wilt bepalen.

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

Speel pauze

Het pictogram "Afspelen/Pauze" wordt altijd weergegeven in de mediamelding en de gerelateerde gebeurtenissen worden automatisch door de browser afgehandeld. Als het standaardgedrag om de een of andere reden niet werkt, kunt u nog steeds de mediagebeurtenissen 'Afspelen' en 'Pauze' afhandelen.

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

Meldingen overal

Het leuke van de Media Session API is dat het meldingenvak niet de enige plaats is waar media-metagegevens en bedieningselementen zichtbaar zijn. De mediamelding wordt automatisch gesynchroniseerd met elk gekoppeld draagbaar apparaat. En het verschijnt ook op vergrendelschermen.

Vergrendel scherm
Vergrendelscherm - Foto door Michael Alø-Nielsen / CC BY 2.0
Slijtagemelding
Slijtagemelding

Zorg ervoor dat het offline leuk speelt

Ik weet wat je nu denkt. Servicemedewerker schiet te hulp!

Dat is waar, maar eerst en vooral wilt u er zeker van zijn dat alle items in deze checklist zijn gecontroleerd :

  • Alle media- en artworkbestanden worden geleverd met de juiste Cache-Control HTTP-header. Hierdoor kan de browser eerder opgehaalde bronnen in het cachegeheugen opslaan en opnieuw gebruiken. Zie de Caching-checklist .
  • Zorg ervoor dat alle media- en illustratiebestanden worden aangeboden met de Allow-Control-Allow-Origin: * HTTP-header. Hierdoor kunnen webapps van derden HTTP-antwoorden van uw webserver ophalen en gebruiken.

De cachingstrategie voor servicemedewerkers

Wat mediabestanden betreft, raad ik een eenvoudige " Cache, terugvallen op netwerk "-strategie aan, zoals geïllustreerd door Jake Archibald.

Voor illustraties zou ik echter iets specifieker zijn en de onderstaande aanpak kiezen:

  • If al een kunstwerk in de cache zit, serveer het dan vanuit de cache
  • Haal Else illustraties op van het netwerk
    • If het ophalen succesvol is, voeg dan netwerkillustraties toe aan de cache en serveer deze
    • Else serveer je de fallback-illustraties uit de cache

Op die manier hebben mediameldingen altijd een mooi artwork-pictogram, zelfs als de browser ze niet kan ophalen. Hier leest u hoe u dit kunt implementeren:

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

Laat de gebruiker de cache beheren

Terwijl de gebruiker inhoud van uw webapp gebruikt, kunnen media- en illustratiebestanden veel ruimte op zijn apparaat in beslag nemen. Het is uw verantwoordelijkheid om aan te geven hoeveel cache er wordt gebruikt en om gebruikers de mogelijkheid te geven deze te wissen . Gelukkig voor ons is dit vrij eenvoudig met de Cache API .

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

Implementatienotities

  • Chrome voor Android vraagt ​​om 'volledige' audiofocus om mediameldingen alleen weer te geven als de duur van het mediabestand minimaal 5 seconden bedraagt.
  • Meldingsillustraties ondersteunen blob-URL's en gegevens-URL's.
  • Als er geen artwork is gedefinieerd en er is een pictogramafbeelding met de gewenste grootte, zullen mediameldingen hiervan gebruik maken.
  • De grootte van de meldingsafbeelding in Chrome voor Android is 512x512 . Voor goedkope apparaten is dit 256x256 .
  • Sluit mediameldingen af ​​met audio.src = '' .
  • Omdat de Web Audio API om historische redenen niet om Android Audio Focus vraagt, is de enige manier om deze te laten werken met de Media Session API het koppelen van een <audio> -element als invoerbron aan de Web Audio API. Hopelijk zal de voorgestelde Web AudioFocus API de situatie in de nabije toekomst verbeteren.
  • Mediasessieoproepen hebben alleen invloed op mediameldingen als ze uit hetzelfde frame komen als de mediabron. Zie het fragment hieronder.
<iframe id="iframe">
  <audio>...</audio>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Steun

Op het moment van schrijven is Chrome voor Android het enige platform dat de Media Session API ondersteunt. Meer actuele informatie over de browserimplementatiestatus kunt u vinden op Chrome Platform Status .

Monsters & demo's

Bekijk onze officiële Chrome Media Session-voorbeelden met Blender Foundation en het werk van Jan Morgenstern .

Bronnen

Mediasessiespecificatie: wicg.github.io/mediasession

Specificaties: github.com/WICG/mediasession/issues

Chrome-bugs: crbug.com