Medienbenachrichtigungen anpassen und Playlists verwalten

François Beaufort
François Beaufort

Mit der brandneuen Media Session API können Sie jetzt Medienbenachrichtigungen anpassen, indem Sie Metadaten für die Medien angeben, die Ihre Webanwendung abspielt. Außerdem können Sie damit medienbezogene Ereignisse verarbeiten, z. B. eine Suche oder Titeländerungen, die über Benachrichtigungen oder Medienschlüssel ausgelöst wurden. Bist du gespannt? Dann sehen Sie sich die offiziellen Media Sessions-Beispiele an.

Die Media Session API wird in Chrome 57 unterstützt (Beta im Februar 2017, stabil im März 2017).

Kurzfassung der Mediensitzung:
Foto von Michael Alø-Nielsen/ CC BY 2.0

Gib mir, was ich will

Sie kennen die Media Session API bereits und möchten einfach wieder Boilerplate-Code kopieren und einfügen? Hier ist es also.

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

In den Code einbauen

Lass uns spielen 🎷

Fügen Sie Ihrer Webseite ein einfaches <audio>-Element hinzu und weisen Sie mehrere Medienquellen zu, damit der Browser auswählen kann, welche am besten funktioniert.

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

Wie du vielleicht weißt, ist autoplay für Audioelemente in Chrome für Android deaktiviert. Daher müssen wir die play()-Methode des Audioelements verwenden. Diese Methode muss durch eine Nutzergeste ausgelöst werden, z. B. durch eine Berührung oder einen Mausklick. Das bedeutet, dass pointerup-, click- und touchend-Ereignisse überwacht werden. Mit anderen Worten: Der Nutzer muss auf eine Schaltfläche klicken, bevor Ihre Webanwendung tatsächlich Geräusche erzeugen kann.

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

Wenn Sie Audioinhalte nicht direkt nach der ersten Interaktion wiedergeben möchten, empfehlen wir die Verwendung der load()-Methode des Audioelements. Dies ist eine Möglichkeit für den Browser, zu erfassen, ob der Nutzer mit dem Element interagiert hat. Unter Umständen wird die Wiedergabe dadurch eventuell reibungsloser, da die Inhalte bereits geladen sind.

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

Benachrichtigung anpassen

Wenn Ihre Web-App Audio wiedergibt, wird bereits eine Medienbenachrichtigung in der Benachrichtigungsleiste angezeigt. Unter Android versucht Chrome, geeignete Informationen anzuzeigen. Dazu werden der Titel des Dokuments und das größte Symbolbild verwendet, das gefunden werden kann.

Ohne Mediensitzung
Ohne Mediensitzung
Mit Mediensitzung
Mit Mediensitzung

Metadata festlegen

Sehen wir uns an, wie Sie diese Medienbenachrichtigung anpassen können, indem Sie mit der Media Session API einige Metadaten der Mediensitzung wie Titel, Künstler, Albumname und Artwork festlegen.

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

Nach der Wiedergabe müssen Sie die Mediensitzung nicht mehr „freigeben“, da die Benachrichtigung automatisch ausgeblendet wird. Hinweis: Beim Start der Wiedergabe wird die aktuelle navigator.mediaSession.metadata verwendet. Deshalb musst du ihn aktualisieren, damit in den Medienbenachrichtigungen immer relevante Informationen angezeigt werden.

Vorheriger Titel / nächster Titel

Wenn Ihre Webanwendung eine Playlist bereitstellt, können Sie Nutzern die Möglichkeit bieten, direkt von der Medienbenachrichtigung aus durch Ihre Playlist zu navigieren, und zwar mit einigen Symbolen für „Vorheriger Titel“ und „Nächster Titel“.

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

Media Action-Handler bleiben erhalten. Dies ist dem Muster des Event-Listeners sehr ähnlich, außer dass die Verarbeitung eines Ereignisses dazu führt, dass der Browser kein Standardverhalten mehr ausführt und dies als Signal verwendet, dass Ihre Webanwendung die Medienaktion unterstützt. Daher werden Steuerelemente für Medienaktionen nur angezeigt, wenn Sie den richtigen Action-Handler festlegen.

Übrigens ist es genauso einfach, einen Media Action-Handler zu deaktivieren, wie ihn null zuzuweisen.

Zurück-/Vorwärtsspulen

Mit der Media Session API können Sie die Medienbenachrichtigungssymbole „Zurückspulen“ und „Vorwärts springen“ anzeigen, wenn Sie die übersprungene Zeit steuern möchten.

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

Wiedergabe / Pause

Das Symbol „Wiedergabe/Pause“ wird immer in der Medienbenachrichtigung angezeigt und die zugehörigen Ereignisse werden automatisch vom Browser verarbeitet. Sollte das Standardverhalten aus irgendeinem Grund nicht funktionieren, können Sie Medienereignisse mit den Optionen „Wiedergabe“ und „Pause“ trotzdem verarbeiten.

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

Benachrichtigungen überall

Das Tolle an der Media Session API ist, dass die Benachrichtigungsleiste nicht der einzige Ort ist, an dem Medienmetadaten und Steuerelemente sichtbar sind. Die Medienbenachrichtigungen werden automatisch mit jedem gekoppelten Wearable-Gerät synchronisiert. Auch auf Sperrbildschirmen.

Displaysperre:
Sperrbildschirm – Foto von Michael Alø-Nielsen / CC BY 2.0
Wear-Benachrichtigung
Wear-Benachrichtigung

Offline spielen

Ich weiß, was Sie jetzt denken. Service Worker der Rettungsdienst

Richtig, aber in erster Linie möchten Sie sicherstellen, dass alle Punkte in dieser Checkliste geprüft wurden:

  • Alle Medien- und Artwork-Dateien werden mit dem entsprechenden Cache-Control-HTTP-Header bereitgestellt. Dadurch kann der Browser zuvor abgerufene Ressourcen im Cache speichern und wiederverwenden. Siehe Caching-Checkliste.
  • Alle Medien- und Artworkdateien müssen mit dem HTTP-Header Allow-Control-Allow-Origin: * bereitgestellt werden. Dadurch können Webanwendungen von Drittanbietern HTTP-Antworten von Ihrem Webserver abrufen und verarbeiten.

Die Service Worker-Caching-Strategie

In Bezug auf Mediendateien empfehlen wir eine einfache Strategie des Typs Cache, Fallback auf das Netzwerk, wie von Jake Archibald dargestellt.

Für Artwork würde ich jedoch etwas konkreter sein und den folgenden Ansatz wählen:

  • If-Artwork befindet sich bereits im Cache, stelle es aus dem Cache bereit
  • Else ruft Artwork aus dem Netzwerk ab
    • Der If-Abruf ist erfolgreich. Füge dem Cache Netzwerkgrafiken hinzu und stelle sie bereit.
    • Else stellt die Fallback-Grafik aus dem Cache bereit

So haben Medienbenachrichtigungen immer ein schönes Artwork-Symbol, auch wenn der Browser sie nicht abrufen kann. So könnten Sie dies implementieren:

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

Cache durch Nutzer verwalten lassen

Da der Nutzer Inhalte aus Ihrer Web-App nutzt, können Medien- und Grafikdateien auf seinem Gerät viel Speicherplatz einnehmen. Es liegt in Ihrer Verantwortung, zu zeigen, wie viel Cache verwendet wird, und Nutzern die Möglichkeit zu geben, ihn zu leeren. Zum Glück ist dies mit der Cache API ziemlich einfach.

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

Implementierungshinweise

  • Chrome für Android fordert einen vollständigen Audiofokus an, damit Medienbenachrichtigungen nur dann angezeigt werden, wenn die Dauer der Mediendatei mindestens 5 Sekunden beträgt.
  • Benachrichtigungsgrafiken unterstützen Blob- und Daten-URLs.
  • Wenn kein Artwork definiert ist und ein Symbolbild in einer gewünschten Größe vorhanden ist, wird es in Medienbenachrichtigungen verwendet.
  • Die Größe der Benachrichtigungsgrafik in Chrome für Android ist 512x512. Für Low-End-Geräte ist er 256x256.
  • Medienbenachrichtigungen mit audio.src = '' schließen.
  • Da die Web Audio API aus historischen Gründen keinen Android Audio Focus anfordert, kann sie nur mit der Media Session API verwendet werden, indem ein <audio>-Element als Eingabequelle an die Web Audio API angehängt wird. Wir hoffen, dass die vorgeschlagene Web AudioFocus API die Situation in naher Zukunft verbessern wird.
  • Mediensitzungsaufrufe wirken sich nur dann auf Medienbenachrichtigungen aus, wenn sie aus demselben Frame wie die Medienressource stammen. Unten sehen Sie das entsprechende Snippet.
<iframe id="iframe">
  <audio>...</audio>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Support

Derzeit ist Chrome für Android die einzige Plattform, die die Media Session API unterstützt. Aktuelle Informationen zum Status der Browserimplementierung finden Sie unter Status der Chrome-Plattform.

Beispiele und Demos

Sehen Sie sich unsere offiziellen Beispiele für Chrome Media Sessions mit Blender Foundation und Jan Morgensterns Arbeit an.

Ressourcen

Spezifikation für Mediasitzungen: wicg.github.io/mediasession

Spezifikationsprobleme: github.com/WICG/mediasession/issues

Chrome-Fehler: crbug.com