Benutzerkontrolle geben: Die Media Session API

Avatar of Idorenyin Udoh
Idorenyin Udoh am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Hier ist ein Szenario. Sie starten einen krachenden Kendrick Lamar Track in einem Ihrer vielen offenen Browser-Tabs. Sie lieben es, aber jemand kommt herein und Sie müssen ihn pausieren. Welcher Tab ist es? Browser versuchen, dabei ein wenig zu helfen. Sie können wahrscheinlich den gesamten Systemton stummschalten. Aber wäre es nicht schön, die Audiowiedergabe tatsächlich steuern zu können, ohne unbedingt zu diesem Tab zurückkehren zu müssen?

Die Media Session API macht dies möglich. Sie gibt der Wiedergabe von Medien Zugriff für den Benutzer, *außerhalb* des Browser-Tabs, in dem sie abgespielt wird. Wenn sie implementiert ist, wird sie an verschiedenen Stellen auf dem Gerät verfügbar sein, darunter

  • der Benachrichtigungsbereich auf vielen Mobilgeräten,
  • auf anderen Wearables und
  • dem Medien-Hub-Bereich vieler Desktop-Geräte.

Zusätzlich erlaubt uns die Media Session API, die Medienwiedergabe mit Medientasten und Sprachassistenten wie Siri, Google Assistant, Bixby oder Alexa zu steuern.

Die Media Session API

Die Media Session API besteht hauptsächlich aus den folgenden zwei Schnittstellen:

  • MediaMetadata
  • MediaSession

Die MediaMetadata-Schnittstelle liefert Daten über die abgespielten Medien. Sie ist dafür verantwortlich, uns den Titel, das Album, das Artwork und den Künstler (in diesem Beispiel Kendrick Lamar) der Medien mitzuteilen. Die MediaSession-Schnittstelle ist für die Funktionalität der Medienwiedergabe zuständig.

Bevor wir uns eingehend mit dem Thema beschäftigen, müssen wir die Feature-Erkennung berücksichtigen. Es ist gute Praxis zu prüfen, ob ein Browser ein Feature unterstützt, bevor man es implementiert. Um zu prüfen, ob ein Browser die Media Session API unterstützt, müssten wir Folgendes in unsere JavaScript-Datei aufnehmen:

if ('mediaSession' in navigator) {
  // Our media session api that lets us seek to the beginning of Kendrick Lamar's "Alright"
}

Die MediaMetadata-Schnittstelle

Der Konstruktor, MediaMetadata.MediaMetadata(), erstellt ein neues MediaMetadata-Objekt. Nach dessen Erstellung können wir die folgenden Eigenschaften hinzufügen:

  • MediaMetadata.title legt den Titel der abgespielten Medien fest oder ruft ihn ab.
  • MediaMetadata.artist legt den Namen des Künstlers oder der Gruppe der abgespielten Medien fest oder ruft ihn ab.
  • MediaMetadata.album legt den Namen des Albums fest, das die abgespielten Medien enthält, oder ruft ihn ab.
  • MediaMetadata.artwork legt das Array von Bildern fest, die mit den abgespielten Medien verknüpft sind, oder ruft es ab.

Der Wert der artwork-Eigenschaft des MediaMetadata-Objekts ist ein Array von MediaImage-Objekten. Ein MediaImage-Objekt enthält Details, die ein Bild beschreiben, das mit den Medien verknüpft ist. Die Objekte haben die folgenden drei Eigenschaften:

  • src: die URL des Bildes
  • sizes: gibt die Größe des Bildes an, damit ein Bild nicht skaliert werden muss
  • type: der MIME-Typ des Bildes

Erstellen wir ein MediaMetadata-Objekt für Kendrick Lamars „Alright“ aus seinem Album To Pimp a Butterfly.

if ('mediaSession' in navigator) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Alright',
    artist: 'Kendrick Lamar',
    album: 'To Pimp A Butterfly',
    artwork: [
      { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/96x96', sizes: '96x96', type: 'image/png' },
      { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/128x128', sizes: '128x128', type: 'image/png' },
      // More sizes, like 192x192, 256x256, 384x384, and 512x512
    ]
  });
}

Die MediaSession-Schnittstelle

Wie bereits erwähnt, ermöglicht dies dem Benutzer die Steuerung der Wiedergabe der Medien. Über diese Schnittstelle können wir folgende Aktionen auf den abgespielten Medien ausführen:

  • play: die Medien abspielen
  • pause: die Medien pausieren
  • previoustrack: zum vorherigen Titel wechseln
  • nexttrack: zum nächsten Titel wechseln
  • seekbackward: von der aktuellen Position einige Sekunden zurückspringen
  • seekforward: von der aktuellen Position einige Sekunden vorwärts springen
  • seekto: zu einer bestimmten Zeit von der aktuellen Position aus springen
  • stop: die Medienwiedergabe stoppen
  • skipad: die laufende Werbung überspringen, falls vorhanden

Der aufgezählte Typ MediaSessionAction macht diese Aktionen als String-Typen verfügbar. Um eine dieser Aktionen zu unterstützen, müssen wir die Methode setActionHandler() von MediaSession verwenden, um einen Handler für diese Aktion zu definieren. Die Methode nimmt die Aktion und eine Callback-Funktion entgegen, die aufgerufen wird, wenn der Benutzer die Aktion auslöst. Lassen Sie uns einen nicht zu tiefen Einblick nehmen, um es besser zu verstehen.

Um Handler für die Aktionen play und pause festzulegen, nehmen wir Folgendes in unsere JavaScript-Datei auf:

let alright = new HTMLAudioElement();

if ('mediaSession' in navigator) {
  navigator.mediaSession.setActionHandler('play', () => {
    alright.play();
  });
  navigator.mediaSession.setActionHandler('pause', () => {
    alright.pause();
  });
}

Hier legen wir fest, dass der Titel abgespielt wird, wenn der Benutzer ihn über die Media-Oberfläche abspielt, und pausiert wird, wenn der Benutzer ihn pausiert.

Für die Aktionen previoustrack und nexttrack nehmen wir Folgendes auf:

let u = new HTMLAudioElement();
let forSaleInterlude = new HTMLAudioElement();

if ('mediaSession' in navigator) {
  navigator.mediaSession.setActionHandler('previoustrack', () => {
    u.play();
  });
  navigator.mediaSession.setActionHandler('nexttrack', () => {
    forSaleInterlude.play();
  });
}

Dies ist möglicherweise nicht vollständig selbsterklärend, wenn Sie kein großer Kendrick Lamar-Fan sind, aber hoffentlich verstehen Sie das Wesentliche. Wenn der Benutzer den vorherigen Titel abspielen möchte, stellen wir den vorherigen Titel zum Abspielen ein. Wenn es sich um den nächsten Titel handelt, ist es der nächste Titel.

Um die Aktionen seekbackward und seekforward zu implementieren, nehmen wir Folgendes auf:

if ('mediaSession' in navigator) {
  navigator.mediaSession.setActionHandler('seekbackward', (details) => {
    alright.currentTime = alright.currentTime - (details.seekOffset || 10);
  });
  navigator.mediaSession.setActionHandler('seekforward', (details) => {
    alright.currentTime = alright.currentTime + (details.seekOffset || 10);
  });
}

Da ich keines davon für selbsterklärend halte, möchte ich eine prägnante Erklärung zu den Aktionen seekbackward und seekforward geben. Die Handler für beide Aktionen, seekbackward und seekforward, werden, wie ihre Namen vermuten lassen, ausgelöst, wenn der Benutzer um einige Sekunden zurück- oder vorwärts springen möchte. Das MediaSessionActionDetails-Dictionary liefert uns die „einige Sekunden“ in einer Eigenschaft, seekOffset. Die seekOffset-Eigenschaft ist jedoch nicht immer vorhanden, da nicht alle User Agents gleich agieren. Wenn sie nicht vorhanden ist, sollten wir den Titel so einstellen, dass er um eine für uns sinnvolle Anzahl von Sekunden vor- oder zurückspringt. Daher verwenden wir 10 Sekunden, da dies eine beträchtliche Anzahl ist. Kurz gesagt, wir stellen den Titel so ein, dass er um seekOffset Sekunden springt, wenn dies angegeben ist. Wenn es nicht angegeben ist, springen wir um 10 Sekunden.

Um die seekto-Funktionalität zu unserer Media Session API hinzuzufügen, nehmen wir den folgenden Snippet auf:

if ('mediaSession' in navigator) {
  navigator.mediaSession.setActionHandler('seekto', (details) => {
    if (details.fastSeek && 'fastSeek' in alright) {
      alright.fastSeek(details.seekTime);
      return;
    }
    alright.currentTime = details.seekTime;
  });
}

Hier liefert das MediaSessionActionDetails-Dictionary die Eigenschaften fastSeek und seekTime. fastSeek ist im Grunde ein schnelles Springen (wie Vorspulen oder Zurückspulen), während seekTime die Zeit ist, zu der der Titel springen soll. Während fastSeek eine optionale Eigenschaft ist, liefert das MediaSessionActionDetails-Dictionary immer die seekTime-Eigenschaft für den seekto-Aktionshandler. Grundsätzlich stellen wir den Titel auf fastSeek auf die seekTime ein, wenn die Eigenschaft verfügbar ist und der Benutzer schnell springt, während wir ihn einfach auf die seekTime einstellen, wenn der Benutzer einfach zu einer bestimmten Zeit springt.

Obwohl ich nicht wüsste, warum jemand einen Kendrick-Song stoppen möchte, schadet es nicht, den stop-Aktionshandler der MediaSession-Schnittstelle zu beschreiben.

if ('mediaSession' in navigator) {
  navigator.mediaSession.setActionHandler('stop', () => {
    alright.pause();
    alright.currentTime = 0;
  });
} 

Der Benutzer löst den skipad-Aktionshandler (im Sinne von „Werbung überspringen“ statt „Ski-Pad“) aus, wenn eine Werbung abgespielt wird und er sie überspringen möchte, um weiter Kendrick Lamars „Alright-Titel hören zu können. Wenn ich ehrlich bin, sind die vollständigen Details des skipad-Aktionshandlers außerhalb des Rahmens meines Verständnisses der „Media Session API“. Daher sollten Sie dies wahrscheinlich nach dem Lesen dieses Artikels selbst nachschlagen, wenn Sie es tatsächlich implementieren möchten.

Zusammenfassung

Wir sollten uns etwas merken. Immer wenn der Benutzer den Titel abspielt, springt oder die Wiedergaberate ändert, müssen wir den Positionsstatus auf der von der Media Session API bereitgestellten Oberfläche aktualisieren. Um dies zu implementieren, verwenden wir die Methode setPositionState() des mediaSession-Objekts, wie im Folgenden dargestellt:

if ('mediaSession' in navigator) {
  navigator.mediaSession.setPositionState({
    duration: alright.duration,
    playbackRate: alright.playbackRate,
    position: alright.currentTime
  });
}

Darüber hinaus möchte ich Sie daran erinnern, dass nicht alle Browser der Benutzer alle Aktionen unterstützen. Daher wird empfohlen, die Aktionshandler in einem try...catch-Block festzulegen, wie im Folgenden dargestellt:

const actionsAndHandlers = [
  ['play', () => { /*...*/ }],
  ['pause', () => { /*...*/ }],
  ['previoustrack', () => { /*...*/ }],
  ['nexttrack', () => { /*...*/ }],
  ['seekbackward', (details) => { /*...*/ }],
  ['seekforward', (details) => { /*...*/ }],
  ['seekto', (details) => { /*...*/ }],
  ['stop', () => { /*...*/ }]
]
 
for (const [action, handler] of actionsAndHandlers) {
  try {
    navigator.mediaSession.setActionHandler(action, handler);
  } catch (error) {
    console.log(`The media session action, ${action}, is not supported`);
  }
}

Wenn wir alles bisherige zusammenfügen, erhalten wir Folgendes:

let alright = new HTMLAudioElement();
let u = new HTMLAudioElement();
let forSaleInterlude = new HTMLAudioElement();

const updatePositionState = () => {
  navigator.mediaSession.setPositionState({
    duration: alright.duration,
    playbackRate: alright.playbackRate,
    position: alright.currentTime
  });
}
 
const actionsAndHandlers = [
  ['play', () => {
    alright.play();
    updatePositionState();
  }],
  ['pause', () => { alright.pause(); }],
  ['previoustrack', () => { u.play(); }],
  ['nexttrack', () => { forSaleInterlude.play(); }],
  ['seekbackward', (details) => {
    alright.currentTime = alright.currentTime - (details.seekOffset || 10);
    updatePositionState();
  }],
  ['seekforward', (details) => {
    alright.currentTime = alright.currentTime + (details.seekOffset || 10);
    updatePositionState();
  }],
  ['seekto', (details) => {
    if (details.fastSeek && 'fastSeek' in alright) {
      alright.fastSeek(details.seekTime);
      updatePositionState();
      return;
    }
    alright.currentTime = details.seekTime;
    updatePositionState();
  }],
  ['stop', () => {
    alright.pause();
    alright.currentTime = 0;
  }],
]
 
if ( 'mediaSession' in navigator ) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Alright',
    artist: 'Kendrick Lamar',
    album: 'To Pimp A Butterfly',
    artwork: [
      { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/96x96', sizes: '96x96', type: 'image/png' },
      { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/128x128', sizes: '128x128', type: 'image/png' },
      // More sizes, like 192x192, 256x256, 384x384, and 512x512
    ]
  });
 
  for (const [action, handler] of actionsAndHandlers) {
    try {
      navigator.mediaSession.setActionHandler(action, handler);
    } catch (error) {
      console.log(`The media session action, ${action}, is not supported`);
    }
  }
}

Hier ist eine Demo der API:

Ich habe sechs der Aktionen implementiert. Fühlen Sie sich frei, die restlichen in Ihrer Freizeit auszuprobieren.

Wenn Sie sich den Pen auf Ihrem Mobilgerät ansehen, achten Sie darauf, wie er in Ihrem Benachrichtigungsbereich angezeigt wird.

Wenn Ihre Smartwatch mit Ihrem Gerät gekoppelt ist, werfen Sie einen Blick darauf.

Wenn Sie den Pen auf Chrome auf dem Desktop anzeigen, navigieren Sie zum Medien-Hub und spielen Sie mit den Medientasten dort herum. Die Demo hat sogar mehrere Titel, sodass Sie das Vorwärts-/Rückwärtsspringen zwischen den Titeln ausprobieren können.

Wenn Sie es bis hierher geschafft haben (oder auch nicht), danke fürs Lesen, und bitte implementieren Sie diese API bei Ihrer nächsten App mit Medienfunktionalität.