Dengan Media Session API yang baru, kini Anda dapat menyesuaikan notifikasi media dengan menyediakan metadata untuk media yang sedang diputar aplikasi web Anda. API ini juga memungkinkan Anda menangani peristiwa terkait media seperti mencari atau melacak perubahan yang mungkin berasal dari notifikasi atau tombol media. Tertarik? Coba contoh Sesi Media resmi.
Media Session API didukung di Chrome 57 (beta pada Februari 2017, stabil pada Maret 2017).
Beri saya apa yang saya inginkan
Anda sudah mengetahui Media Session API dan hanya kembali untuk menyalin dan menempelkan beberapa kode boilerplate tanpa malu? Jadi ini dia.
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() {});
}
Mempelajari kode
Ayo main 🎷
Tambahkan elemen <audio>
sederhana ke halaman web Anda dan tetapkan beberapa sumber media sehingga browser dapat memilih sumber yang paling sesuai.
<audio controls>
<source src="audio.mp3" type="audio/mp3"/>
<source src="audio.ogg" type="audio/ogg"/>
</audio>
Seperti yang mungkin Anda ketahui, autoplay
dinonaktifkan untuk elemen audio di Chrome
untuk Android, yang berarti kita harus menggunakan metode play()
dari elemen
audio. Metode ini harus dipicu oleh gestur pengguna seperti sentuhan atau
klik mouse.
Artinya, memproses peristiwa
pointerup
, click
, dan touchend
. Dengan kata lain, pengguna harus mengklik tombol sebelum aplikasi web Anda benar-benar dapat
membuat derau.
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) });
});
Jika tidak ingin memutar audio tepat setelah interaksi pertama, sebaiknya
gunakan metode load()
dari elemen audio. Hal ini adalah salah satu cara bagi browser untuk melacak apakah pengguna berinteraksi dengan elemen. Perhatikan
bahwa tindakan ini juga dapat membantu melancarkan pemutaran karena konten sudah
dimuat.
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) });
});
Menyesuaikan notifikasi
Saat aplikasi web memutar audio, Anda sudah dapat melihat notifikasi media di baki notifikasi. Di Android, Chrome melakukan yang terbaik untuk menampilkan informasi yang sesuai dengan menggunakan judul dokumen dan gambar ikon terbesar yang dapat ditemukannya.
Menetapkan metadata
Mari kita lihat cara menyesuaikan notifikasi media ini dengan menetapkan beberapa metadata sesi media seperti judul, artis, nama album, dan karya seni menggunakan 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' },
]
});
}
Setelah pemutaran selesai, Anda tidak perlu "melepaskan" sesi media karena
notifikasi akan otomatis hilang. Perlu diingat bahwa navigator.mediaSession.metadata
saat ini akan digunakan saat pemutaran dimulai. Itulah
alasan Anda perlu memperbaruinya untuk memastikan Anda selalu menampilkan informasi
yang relevan dalam notifikasi media.
Lagu sebelumnya / lagu berikutnya
Jika aplikasi web Anda menyediakan playlist, sebaiknya izinkan pengguna menjelajahi playlist langsung dari notifikasi media dengan beberapa ikon "Lagu Sebelumnya" dan "Lagu Berikutnya".
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();
});
Perhatikan bahwa pengendali tindakan media akan tetap ada. Hal ini sangat mirip dengan pola pemroses peristiwa, kecuali bahwa menangani peristiwa berarti browser berhenti melakukan perilaku default dan menggunakan ini sebagai sinyal bahwa aplikasi web Anda mendukung tindakan media. Oleh karena itu, kontrol tindakan media tidak akan ditampilkan kecuali jika Anda menetapkan pengendali tindakan yang sesuai.
Sekadar informasi, membatalkan setelan pengendali tindakan media semudah menetapkannya ke null
.
Mundur / maju
Media Session API memungkinkan Anda menampilkan ikon notifikasi media "Seek Backward" dan "Seek Forward" jika ingin mengontrol jumlah waktu yang dilewati.
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);
});
Memutar / menjeda
Ikon "Putar/Jeda" selalu ditampilkan di notifikasi media dan peristiwa terkait ditangani secara otomatis oleh browser. Jika karena alasan tertentu perilaku default tidak berfungsi, Anda tetap dapat menangani peristiwa media "Putar" dan "Jeda".
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...
});
Notifikasi di mana saja
Hal yang menarik dari Media Session API adalah baki notifikasi bukan satu-satunya tempat di mana metadata dan kontrol media terlihat. Notifikasi media disinkronkan secara otomatis ke perangkat wearable yang disambungkan. Dan itu juga muncul di layar kunci.
Buat video menarik saat offline
Saya tahu apa yang Anda pikirkan sekarang. Pekerja layanan dapat membantu.
Benar, tetapi yang terpenting, Anda ingin memastikan semua item dalam checklist ini telah dicentang:
- Semua file media dan poster ditayangkan dengan header HTTP
Cache-Control
yang sesuai. Tindakan ini akan memungkinkan browser menyimpan dalam cache dan menggunakan kembali resource yang diambil sebelumnya. Lihat Checklist penyimpanan dalam cache. - Pastikan semua file media dan karya seni ditayangkan dengan
header HTTP
Allow-Control-Allow-Origin: *
. Hal ini akan memungkinkan aplikasi web pihak ketiga mengambil dan menggunakan respons HTTP dari server web Anda.
Strategi caching pekerja layanan
Sehubungan dengan file media, sebaiknya gunakan strategi "Cache, kembali ke jaringan" sederhana seperti yang diilustrasikan oleh Jake Archibald.
Namun, untuk karya seni, saya akan sedikit lebih spesifik dan memilih pendekatan di bawah:
- Poster
If
sudah ada di cache, tayangkan dari cache Else
mengambil poster dari jaringan- Pengambilan
If
berhasil, tambahkan poster jaringan ke cache dan tayangkan Else
menayangkan poster penggantian dari cache
- Pengambilan
Dengan demikian, notifikasi media akan selalu memiliki ikon poster yang bagus meskipun browser tidak dapat mengambilnya. Berikut cara menerapkannya:
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));
}
Izinkan pengguna mengontrol cache
Saat pengguna menggunakan konten dari aplikasi web Anda, file media dan karya seni dapat menghabiskan banyak ruang di perangkat mereka. Anda bertanggung jawab untuk menunjukkan berapa banyak cache yang digunakan dan memberi pengguna kemampuan untuk menghapusnya. Untungnya bagi kita, melakukannya cukup mudah dengan 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); });
Catatan penerapan
- Chrome untuk Android meminta fokus audio "penuh" untuk menampilkan notifikasi media hanya jika durasi file media adalah setidaknya 5 detik.
- Poster notifikasi mendukung URL blob dan URL data.
- Jika tidak ada karya seni yang ditentukan dan ada gambar ikon dengan ukuran yang diinginkan, notifikasi media akan menggunakannya.
- Ukuran poster notifikasi di Chrome untuk Android adalah
512x512
. Untuk perangkat kelas bawah, nilainya adalah256x256
. - Tutup notifikasi media dengan
audio.src = ''
. - Karena Web Audio API tidak meminta Fokus Audio Android karena alasan
historis, satu-satunya cara untuk membuatnya berfungsi dengan Media Session API adalah dengan menghubungkan
elemen
<audio>
sebagai sumber input ke Web Audio API. Semoga Web AudioFocus API yang diusulkan akan memperbaiki situasi ini dalam waktu dekat. - Panggilan Sesi Media hanya akan memengaruhi notifikasi media jika berasal dari frame yang sama dengan resource media. Lihat cuplikan di bawah.
<iframe id="iframe">
<audio>...</audio>
</iframe>
<script>
iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
...
});
</script>
Dukungan
Pada saat penulisan, Chrome untuk Android adalah satu-satunya platform yang mendukung Media Session API. Informasi terbaru tentang status implementasi browser dapat ditemukan di Status Platform Chrome.
Contoh & demo
Lihat contoh Sesi Media Chrome resmi kami yang menampilkan Blender Foundation dan karya Jan Morgenstern.
Resource
Spesifikasi Sesi Media: wicg.github.io/mediasession
Masalah Spesifikasi: github.com/WICG/mediasession/issues
Bug Chrome: crbug.com