Dzięki zupełnie nowemu interfejsowi Media Session API możesz teraz dostosowywać powiadomienia o multimediach przez udostępnienie metadanych multimediów odtwarzanych w Twojej aplikacji internetowej. Umożliwia też obsługę zdarzeń związanych z multimediami, takich jak przewijanie lub śledzenie zmian, które mogą pochodzić z powiadomień lub klawiszy multimedialnych. Brzmi dobrze? Wypróbuj oficjalne przykłady sesji medialnych.
Interfejs Media Session API jest obsługiwany w Chrome 57 (wersja beta w lutym 2017 r.; wersja stabilna w marcu 2017 r.).
Daj mi to, co chcę
Znasz już interfejs Media Session API, ale wracasz do kopiowania i wklejania bez wstydu? Gotowe.
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() {});
}
Wejdź do kodu
Zagrajmy 🎷
Dodaj do swojej strony internetowej prosty element <audio>
i przypisz kilka źródeł multimediów, by przeglądarka mogła wybrać to, które sprawdza się najlepiej.
<audio controls>
<source src="audio.mp3" type="audio/mp3"/>
<source src="audio.ogg" type="audio/ogg"/>
</audio>
Jak być może wiesz, w Chrome na Androida funkcja autoplay
jest wyłączona w przypadku elementów audio, co oznacza, że musimy używać metody play()
elementu audio. Ta metoda musi być aktywowana przez gest użytkownika, np. dotknięcie lub kliknięcie myszą.
Oznacza to odsłuchiwanie zdarzeń pointerup
, click
i touchend
. Inaczej mówiąc, użytkownik musi kliknąć przycisk, aby Twoja aplikacja internetowa mogła wydać dźwięk.
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) });
});
Jeśli nie chcesz odtwarzać dźwięku od razu po pierwszej interakcji, zalecamy użycie metody load()
elementu audio. Dzięki temu przeglądarka może śledzić, czy użytkownik wszedł w interakcję z elementem. Pamiętaj, że może to też pomóc w zapewnieniu płynności odtwarzania, bo treści zostaną już wczytane.
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) });
});
Dostosowywanie powiadomienia
Gdy Twoja aplikacja internetowa odtwarza dźwięk, w obszarze powiadomień zobaczysz powiadomienie o multimediach. Na Androidzie Chrome stara się pokazać odpowiednie informacje, używając tytułu dokumentu i największej ikony, jaką może znaleźć.
Ustaw metadane
Zobaczmy, jak dostosować to powiadomienie o multimediach, ustawiając metadane sesji multimedialnej, np. tytuł, wykonawcę, nazwę albumu i grafikę, za pomocą interfejsu 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' },
]
});
}
Po zakończeniu odtwarzania nie musisz „zwalniać” sesji multimediów, ponieważ powiadomienie automatycznie zniknie. Pamiętaj, że bieżąca wartość navigator.mediaSession.metadata
zostanie użyta przy rozpoczęciu odtwarzania. Dlatego musisz go zaktualizować, aby mieć pewność, że w powiadomieniach multimedialnych zawsze pojawiają się istotne informacje.
Poprzedni utwór / następny utwór
Jeśli Twoja aplikacja internetowa zawiera playlistę, możesz pozwolić użytkownikowi na poruszanie się po niej bezpośrednio z powiadomień o multimediach za pomocą ikon „Poprzedni utwór” i „Następny utwór”.
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();
});
Pamiętaj, że moduły obsługi działań związanych z multimediami nie zostaną usunięte. Działa to bardzo podobnie do wzorca odbiornika, z tym że obsługa zdarzenia oznacza, że przeglądarka przestaje wykonywać domyślne działania i używa go jako sygnału, że aplikacja internetowa obsługuje akcję multimedialną. Ustawienia działań związanych z multimediami nie będą więc widoczne, jeśli nie ustawisz odpowiedniego modułu.
Przy okazji – anulowanie ustawienia modułu obsługi działań związanych z multimediami jest równie łatwe jak przypisanie go do: null
.
Przewijanie do tyłu lub do przodu
Interfejs Media Session API pozwala wyświetlać ikony powiadomień o multimediach „Przewiń do tyłu” i „Przewiń do przodu”, jeśli chcesz kontrolować ilość czasu pomijanego.
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);
});
Odtwarzanie / wstrzymywanie
W powiadomieniu o multimediach jest zawsze widoczna ikona „Odtwórz/wstrzymaj”, a powiązane z nią zdarzenia są obsługiwane automatycznie przez przeglądarkę. Jeśli z jakiegoś powodu domyślne działanie nie przyniesie oczekiwanych rezultatów, nadal możesz obsługiwać zdarzenia multimedialne „Odtwórz” i „Wstrzymaj”.
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...
});
Powiadomienia wszędzie
Ciekawe w interfejsie Media Session API jest to, że obszar powiadomień nie jest jedynym miejscem, w którym widoczne są metadane i elementy sterujące. Powiadomienie o multimediach jest automagicznie synchronizowane z dowolnym sparowanym urządzeniem do noszenia. Wyświetla się też na ekranie blokady.
Dobrze baw się offline
Wiem, co teraz myślisz. Serwisant na ratunek!
Prawda, ale przede wszystkim sprawdź, czy wszystkie elementy na tej liście kontrolnej są zaznaczone:
- Wszystkie pliki multimedialne i grafiki mają odpowiedni nagłówek HTTP
Cache-Control
. Dzięki temu przeglądarka będzie mogła buforować i ponownie wykorzystywać pobrane wcześniej zasoby. Zobacz Listę kontrolną dotyczącą buforowania. - Upewnij się, że wszystkie pliki multimedialne i grafiki mają nagłówek HTTP
Allow-Control-Allow-Origin: *
. Dzięki temu aplikacje internetowe innych firm będą mogły pobierać i wykorzystywać odpowiedzi HTTP z Twojego serwera WWW.
Strategia buforowania skryptu service worker
Jeśli chodzi o pliki multimedialne, polecamy prostą strategię „Cache, fasting back to network”, zgodnie z ilustracją Jake'a Archibalda.
W przypadku elementów graficznych użyję więcej szczegółów i wybierz metodę poniżej:
- Dzieło sztuki
If
jest już w pamięci podręcznej, wyświetl je z pamięci podręcznej Else
pobierze grafikę z sieci- Plik
If
został pobrany, dodaj grafikę sieci do pamięci podręcznej i wyświetl ją Else
wyświetla grafikę zastępczą z pamięci podręcznej
- Plik
Dzięki temu powiadomienia o multimediach zawsze będą miały ładną ikonę grafiki, nawet jeśli przeglądarka nie może ich pobrać. Aby to zrobić:
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));
}
Pozwól użytkownikowi na kontrolowanie pamięci podręcznej
Gdy użytkownik korzysta z treści z Twojej aplikacji internetowej, pliki multimedialne i grafiki mogą zajmować dużo miejsca na urządzeniu. To Ty ponosisz odpowiedzialność za pokazanie, ile pamięci podręcznej jest używana, i zapewnienie użytkownikom możliwości jej wyczyszczenia. Na szczęście za pomocą interfejsu Cache API jest to całkiem łatwe.
// 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); });
Uwagi dotyczące implementacji
- Chrome na Androida prosi o „pełne” zaznaczenie dźwięku, aby pokazywać powiadomienia o multimediach tylko wtedy, gdy czas trwania pliku multimedialnego wynosi co najmniej 5 sekund.
- Grafika powiadomień obsługuje adresy URL obiektów blob i adresów URL danych.
- Jeśli nie zdefiniowano żadnej grafiki, a obraz ikony ma odpowiedni rozmiar, będzie używany w powiadomieniach o multimediach.
- Rozmiar grafiki powiadomień w Chrome na Androida to
512x512
. W przypadku urządzeń mniej zaawansowanych jest to256x256
. - Zamknij powiadomienia o multimediach na urządzeniu
audio.src = ''
. - Interfejs Web Audio API nie wysyła żądania Android Audio Focus z przyczyn historycznych, dlatego jedynym sposobem na umożliwienie jego współdziałania z interfejsem Media Session API jest podłączenie elementu
<audio>
jako źródła danych wejściowych do interfejsu Web Audio API. Mamy nadzieję, że proponowany interfejs Web AudioFocus API pomoże w niedalekiej przyszłości poprawić sytuację. - Wywołania sesji multimediów mają wpływ na powiadomienia o multimediach tylko wtedy, gdy pochodzą z tej samej klatki co zasób multimedialny. Zobacz ten fragment kodu.
<iframe id="iframe">
<audio>...</audio>
</iframe>
<script>
iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
...
});
</script>
Pomoc
Obecnie Chrome na Androida jest jedyną platformą, która obsługuje interfejs Media Session API. Aktualne informacje o stanie implementacji przeglądarki znajdziesz w artykule Stan platformy Chrome.
Sample i wersje demonstracyjne
Zobacz nasze oficjalne przykłady sesji multimedialnych w Chrome, w których m.in. Blender Foundation i dzieła Jana Morgensterna.
Zasoby
Specyfikacja sesji multimediów: wicg.github.io/mediasession
Problemy ze specyfikacją: github.com/WICG/mediasession/issues
Błędy w Chrome: crbug.com