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 in Ihrer Webanwendung wiedergegeben werden. Außerdem können Sie medienbezogene Ereignisse verarbeiten, z. B. das Suchen oder Ändern von Titeln, die von Benachrichtigungen oder Medientasten stammen. Bist du gespannt? Probiere die offiziellen Beispiele für Mediensitzungen aus.

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

Zusammenfassung für Mediensitzungen:
Foto von Michael Alø-Nielsen / CC BY 2.0

Gimme what I want

Sie kennen die Media Session API bereits und kopieren und fügen einfach Boilerplate-Code ein? Hier ist es.

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

Code

Lass uns spielen 🎷

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

<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. Das bedeutet, dass wir die play()-Methode des Audioelements verwenden müssen. Diese Methode muss durch eine Nutzergeste wie ein Tippen oder Mausklick ausgelöst werden. Das bedeutet, dass pointerup-, click- und touchend-Ereignisse überwacht werden müssen. Mit anderen Worten: Der Nutzer muss auf eine Schaltfläche klicken, bevor Ihre Webanwendung Töne ausgeben 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 abspielen möchten, empfehle ich Ihnen, die Methode load() des Audioelements zu verwenden. So kann der Browser nachverfolgen, ob der Nutzer mit dem Element interagiert hat. Außerdem kann die Wiedergabe dadurch flüssiger ablaufen, 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 in Ihrer Webanwendung Audio abgespielt wird, sehen Sie bereits eine Medienbenachrichtigung in der Benachrichtigungsleiste. Unter Android versucht Chrome, geeignete Informationen anzuzeigen. Dazu verwendet es den Titel des Dokuments und das größte Symbolbild, das es finden kann.

Ohne Mediensitzung
Ohne Mediensitzung
Mit Mediensitzung
Mit Mediensitzung

Metadata festlegen

Sehen wir uns an, wie Sie diese Medienbenachrichtigung anpassen, 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 „freigeben“, da die Benachrichtigung automatisch verschwindet. Beachte, dass der aktuelle navigator.mediaSession.metadata verwendet wird, wenn die Wiedergabe gestartet wird. Daher müssen Sie es aktualisieren, damit in der Medienbenachrichtigung immer relevante Informationen angezeigt werden.

Vorheriger Titel / Nächster Titel

Wenn Ihre Webanwendung eine Playlist enthält, können Sie den Nutzern erlauben, direkt über die Medienbenachrichtigung mithilfe der Symbole „Vorheriger Titel“ und „Nächster Titel“ durch die Playlist zu blättern.

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-Aktions-Handler bleiben erhalten. Das ist dem Ereignis-Listener-Muster sehr ähnlich, mit der Ausnahme, dass beim Bearbeiten eines Ereignisses das Standardverhalten des Browsers beendet wird und dies als Signal verwendet wird, dass Ihre Webanwendung die Medienaktion unterstützt. Daher werden die Medienaktionssteuerungen nur angezeigt, wenn Sie den richtigen Action-Handler festlegen.

Übrigens: Das Rücksetzen eines Media-Action-Handlers ist genauso einfach wie die Zuweisung an null.

Zurückspulen / Vorspulen

Mit der Media Session API können Sie Medienbenachrichtigungssymbole für „Zurückspulen“ und „Vorspulen“ anzeigen, wenn Sie die Zeitspanne steuern möchten, die übersprungen werden soll.

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. Wenn das Standardverhalten aus irgendeinem Grund nicht funktioniert, können Sie weiterhin die Medienereignisse „Wiedergabe“ und „Pause“ 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 Medienmetadaten und -steuerelemente nicht nur im Benachrichtigungs- bzw. Infobereich angezeigt werden. Die Medienbenachrichtigung wird automatisch mit allen gekoppelten Wearables synchronisiert. Außerdem wird sie auf dem Sperrbildschirm angezeigt.

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

Offline wiedergeben

Ich weiß, was Sie jetzt denken. Dienstworker zur Rettung!

Das stimmt, aber in erster Linie sollten Sie darauf achten, dass alle Punkte auf dieser Checkliste angeklickt sind:

  • Alle Medien- und Artwork-Dateien werden mit dem entsprechenden Cache-Control-HTTP-Header bereitgestellt. So kann der Browser zuvor abgerufene Ressourcen im Cache speichern und wiederverwenden. Weitere Informationen finden Sie in der Checkliste für das Caching.
  • Alle Medien- und Artwork-Dateien müssen mit dem HTTP-Header Allow-Control-Allow-Origin: * bereitgestellt werden. So können Webanwendungen von Drittanbietern HTTP-Antworten von Ihrem Webserver abrufen und verwenden.

Die Service Worker-Caching-Strategie

Für Mediendateien empfehle ich eine einfache Cache-Strategie mit Netzwerk-Fallback, wie sie Jake Archibald veranschaulicht hat.

Bei Artwork würde ich jedoch etwas genauer sein und den unten beschriebenen Ansatz wählen:

  • If Artwork ist bereits im Cache, wird aus dem Cache bereitgestellt
  • Else Artwork aus dem Netzwerk abrufen
    • If Abruf war erfolgreich, Netzwerk-Artwork in den Cache aufnehmen und bereitstellen
    • Else Fallback-Artwork aus dem Cache bereitstellen

So haben Medienbenachrichtigungen immer ein schönes Artwork-Symbol, auch wenn der Browser sie nicht abrufen kann. So könnten Sie das 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 vom Nutzer verwalten lassen

Wenn Nutzer Inhalte aus Ihrer Webanwendung nutzen, können Medien- und Artwork-Dateien viel Speicherplatz auf ihrem Gerät belegen. Sie müssen angeben, wie viel Cache verwendet wird, und Nutzern die Möglichkeit geben, ihn zu löschen. Glücklicherweise ist das mit der Cache API ganz 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 nur dann den „vollständigen“ Audiofokus an, um Medienbenachrichtigungen anzuzeigen, wenn die Dauer der Mediendatei mindestens 5 Sekunden beträgt.
  • Für Benachrichtigungs-Artwork werden Blob-URLs und Daten-URLs unterstützt.
  • Wenn kein Artwork definiert ist und es ein Symbolbild in der gewünschten Größe gibt, wird es für Medienbenachrichtigungen verwendet.
  • Die Größe des Artworks für Benachrichtigungen in Chrome für Android beträgt 512x512. Bei Low-End-Geräten ist das 256x256.
  • Schließen Sie Medienbenachrichtigungen mit audio.src = ''.
  • Da die Web Audio API aus historischen Gründen keinen Android-Audiofokus anfordert, ist die einzige Möglichkeit, sie mit der Media Session API zu verwenden, die Verknüpfung eines <audio>-Elements als Eingabequelle mit der Web Audio API. Wir hoffen, dass sich die Situation in naher Zukunft durch die vorgeschlagene Web AudioFocus API verbessern wird.
  • Aufrufe von Media-Sitzungen wirken sich nur auf Medienbenachrichtigungen aus, wenn sie aus demselben Frame wie die Medienressource stammen. Siehe Snippet unten.
<iframe id="iframe">
  <audio>...</audio>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Support

Zum Zeitpunkt der Erstellung dieses Artikels ist Chrome für Android die einzige Plattform, die die Media Session API unterstützt. Aktuelle Informationen zum Status der Browserimplementierung finden Sie unter Chrome-Plattformstatus.

Samples und Demos

Sehen Sie sich unsere offiziellen Chrome-Media-Sitzungsbeispiele mit der Blender Foundation und der Arbeit von Jan Morgenstern an.

Ressourcen

Media Session Spec: wicg.github.io/mediasession

Probleme mit der Spezifikation: github.com/WICG/mediasession/issues

Chrome-Fehler: crbug.com