Dzięki nowemu interfejsowi Media Session API możesz teraz dostosowywać powiadomienia o multimediach, podając metadane multimediów odtwarzanych przez aplikację internetową. Umożliwia też obsługę zdarzeń związanych z multimediami, takich jak przeskakiwanie lub zmiana ścieżki, które mogą pochodzić z powiadomień lub kluczy multimediów. Brzmi dobrze? Wypróbuj oficjalne przykłady sesji multimediów.
Interfejs Media Session API jest obsługiwany w Chrome 57 (wersja beta w lutym 2017 roku, stabilna w marcu 2017 roku).
Gimme what I want
Czy znasz już interfejs MediaSession API i po prostu wracasz do kopiowania i wklejania gotowego kodu? Oto on.
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() {});
}
Poznaj kod
Zagrajmy 🎷
Dodaj do strony internetowej prosty element <audio>
i przypisz do niego kilka źródeł multimediów, aby przeglądarka mogła wybrać to, które działa najlepiej.
<audio controls>
<source src="audio.mp3" type="audio/mp3"/>
<source src="audio.ogg" type="audio/ogg"/>
</audio>
Jak pewnie wiesz, w Chrome na Androida funkcja autoplay
jest wyłączona dla elementów audio, co oznacza, że musimy użyć metody play()
elementu audio. Ta metoda musi być wywoływana przez działanie użytkownika, takie jak dotknięcie lub kliknięcie myszką.
Oznacza to nasłuchiwanie zdarzeń pointerup
, click
i touchend
. Inaczej mówiąc, użytkownik musi kliknąć przycisk, aby aplikacja internetowa mogła wygenerować 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 zaraz po pierwszej interakcji, zalecam użycie metody load()
elementu audio. Jest to jeden ze sposobów, dzięki któremu przeglądarka może śledzić, czy użytkownik wchodził w interakcję z elementem. Pamiętaj, że może to również ułatwić płynne odtwarzanie, ponieważ treści będą już załadowane.
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 aplikacja internetowa odtwarza dźwięk, na pasku powiadomień pojawia się powiadomienie multimedialne. Na urządzeniach z Androidem Chrome stara się wyświetlać odpowiednie informacje, korzystając z tytułu dokumentu i największej znalezionej ikony.
Ustawianie metadanych
Zobaczmy, jak dostosować powiadomienie o multimediów, ustawiając niektóre metadane sesji multimedialnej, takie jak tytuł, wykonawca, nazwa albumu i grafika 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 zniknie automatycznie. Pamiętaj, że podczas odtwarzania będzie używana aktualna wersja navigator.mediaSession.metadata
. Dlatego musisz go zaktualizować, aby mieć pewność, że zawsze wyświetlasz odpowiednie informacje w powiadomieniu o mediach.
Poprzedni utwór / Następny utwór
Jeśli Twoja aplikacja internetowa zawiera playlistę, możesz zezwolić użytkownikowi na poruszanie się po niej bezpośrednio z powiadomienia 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 przetwarzacze działań związanych z multimediami pozostaną bez zmian. Jest to bardzo podobne do wzorca eventListener, z tym że obsługa zdarzenia oznacza, że przeglądarka przestaje wykonywać jakiekolwiek domyślne działanie i używa tego jako sygnału, że Twoja aplikacja internetowa obsługuje działanie dotyczące multimediów. Dlatego opcje działania multimediów nie będą widoczne, dopóki nie skonfigurujesz odpowiedniego modułu obsługi działania.
Odznaczenie przetwarzacza akcji typu Media Action jest tak samo proste jak przypisanie go do null
.
Przewijanie do tyłu / do przodu
Interfejs API sesji multimediów umożliwia wyświetlanie ikon „Przewinąć do tyłu” i „Przewinąć do przodu” w powiadomieniach multimedialnych, jeśli chcesz kontrolować ilość przewiniętego czasu.
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
Ikona „Odtwórz/Wstrzymaj” jest zawsze wyświetlana w powiadomieniu o multimediach, a powiązane zdarzenia są obsługiwane automatycznie przez przeglądarkę. Jeśli z jakiegoś powodu domyślne działanie nie zadziała, nadal możesz obsługiwać zdarzenia „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
Wspaniałą rzeczą w przypadku interfejsu Media Session API jest to, że panel powiadomień to nie jedyne miejsce, w którym są widoczne metadane i opcje sterowania multimediami. Powiadomienie o multimediach jest automatycznie synchronizowane z dowolnym sparowanym urządzeniem do noszenia. Wyświetla się też na ekranie blokady.
Odtwarzanie offline
Wiem, co teraz myślisz. Pracownik obsługi klienta na ratunek!
Zgadza się, ale przede wszystkim musisz się upewnić, że wszystkie pozycje na tej liście są zaznaczone:
- Wszystkie pliki multimedialne i pliki z grafiką są wyświetlane z odpowiednim nagłówkiem HTTP
Cache-Control
. Umożliwi to przeglądarce buforowanie i ponowne używanie wcześniej pobranych zasobów. Zobacz listę kontrolną dotyczącą pamięci podręcznej. - Upewnij się, że wszystkie pliki multimedialne i pliki z grafiką są przesyłane z nagłówkiem 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, polecam prostą strategię Cache, falling back to network (użyj pamięci podręcznej, a potem sięgnij do sieci), którą opisał Jake Archibald.
W przypadku grafiki warto jednak podać nieco więcej szczegółów i wybrać jedną z podanych niżej metod:
- Grafika
If
jest już w pamięci podręcznej, wyświetl ją z pamięci podręcznej Else
pobiera grafikę z sieci.If
pobieranie się powiodło, dodaj elementy graficzne sieci do pamięci podręcznej i wyświetl jeElse
wyświetlanie zastępczego grafiki z pamięci podręcznej.
Dzięki temu powiadomienia o multimediach będą zawsze zawierać ładną ikonę, nawet jeśli przeglądarka nie może ich pobrać. Oto jak 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));
}
Zezwalanie użytkownikowi na kontrolowanie pamięci podręcznej
Gdy użytkownik korzysta z treści w aplikacji internetowej, pliki multimedialne i elementy graficzne mogą zajmować dużo miejsca na urządzeniu. Twoim obowiązkiem jest poinformowanie użytkowników o tym, ile pamięci podręcznej jest używane i jak można ją wyczyścić. Na szczęście można to zrobić za pomocą interfejsu 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); });
Uwagi dotyczące implementacji
- Chrome na Androida prosi o „pełne” skupienie na dźwięku, aby wyświetlać powiadomienia o multimediach, tylko gdy czas trwania pliku multimedialnego wynosi co najmniej 5 sekund.
- Grafika powiadomienia obsługuje adresy URL typu blob i adresy URL danych.
- Jeśli nie zdefiniujesz grafiki, a w Twoim kanale jest ikona o odpowiednim rozmiarze, będzie ona używana w powiadomieniach multimedialnych.
- Rozmiar grafiki powiadomienia w Chrome na Androida to
512x512
. W przypadku urządzeń niższej klasy jest to256x256
. - Zamknij powiadomienia o multimediach za pomocą
audio.src = ''
. - Z historycznych powodów interfejs Web Audio API nie prosi o użycie funkcji Android Audio Focus, więc jedynym sposobem na jego działanie 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 proponowane interfejsy Web AudioFocus API poprawią tę sytuację w najbliższej przyszłości. - Wywołania sesji multimediów będą miały wpływ na powiadomienia multimedialne tylko wtedy, gdy pochodzą z tego samego interfejsu, co zasób multimedialny. Zobacz poniższy fragment kodu.
<iframe id="iframe">
<audio>...</audio>
</iframe>
<script>
iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
...
});
</script>
Pomoc
W momencie pisania tego artykułu Chrome na Androida jest jedyną platformą, która obsługuje interfejs Media Session API. Najnowsze informacje o stanie wdrożenia przeglądarki znajdziesz na stronie Stan platformy Chrome.
Sample i wersje demonstracyjne
Zapoznaj się z oficjalnymi Media Session Samples w Chrome, które obejmują Blender Foundation i twórczość 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