Medya bildirimlerini özelleştirin ve oynatma listelerini yönetin

François Beaufort
François Beaufort

Yepyeni Media Session API ile artık web uygulamanızın oynattığı medya için meta veri sağlayarak medya bildirimlerini özelleştirebilirsiniz. Ayrıca, bildirimlerden veya medya tuşlarından gelebilecek sarma veya parça değiştirme gibi medya ile ilgili etkinlikleri yönetmenize olanak tanır. Heyecanlandınız mı? Resmi medya oturumu örneklerini deneyin.

Media Session API'si Chrome 57'de desteklenir (Beta sürümü Şubat 2017'de, Mart 2017'de mevcuttur).

Medya oturumu özeti;
Fotoğraf: Michael Alø-Nielsen / CC BY 2.0

İstediğim şeyi ver

MediaSession API'yi zaten biliyor ve utanmadan bazı standart kodları kopyalayıp yapıştırmak için mi geri geldiniz? İşte burada.

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

Kodu öğrenin

Haydi oynayalım 🎷

Web sayfanıza basit bir <audio> öğesi ekleyin ve tarayıcının en iyi performansı sunanı seçebilmesi için birkaç medya kaynağı atayın.

<audio controls>
    <source src="audio.mp3" type="audio/mp3"/>
    <source src="audio.ogg" type="audio/ogg"/>
</audio>

Bildiğiniz gibi autoplay, Android için Chrome'da ses öğeleri için devre dışı bırakılmıştır. Bu, ses öğesinin play() yöntemini kullanmamız gerektiği anlamına gelir. Bu yöntem, dokunma veya fare tıklaması gibi bir kullanıcı hareketiyle tetiklenmelidir. Yani pointerup, click ve touchend etkinliklerini dinlemek Diğer bir deyişle, web uygulamanız ses çıkarabilmek için kullanıcının bir düğmeyi tıklaması gerekir.

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

İlk etkileşimden hemen sonra ses çalmayı istemiyorsanız ses öğesinin load() yöntemini kullanmanızı öneririz. Bu, tarayıcının kullanıcının öğeyle etkileşimde bulunup bulunmadığını izlemesinin bir yoludur. İçerik önceden yüklendiği için oynatmanın sorunsuz olmasını da sağlayabileceğini unutmayın.

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

Bildirimi özelleştirme

Web uygulamanız ses çalarken, bildirim tepsisinde bir medya bildirimi görüyorsunuz. Android'de Chrome, dokümanın başlığını ve bulabildiği en büyük simge resmini kullanarak uygun bilgileri göstermek için elinden geleni yapar.

Medya oturumu olmadan
Medya oturumu olmadan
Medya oturumuyla
Medya oturumuyla

Meta verileri ayarlama

Media Session API ile başlık, sanatçı, albüm adı ve poster gibi bazı medya oturumu meta verilerini ayarlayarak bu medya bildirimini nasıl özelleştireceğinizi görelim.

// 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' },
    ]
    });
}

Oynatma işlemi tamamlandığında bildirim otomatik olarak kaybolacağından medya oturumunu "bırakmanız" gerekmez. Oynatma başladığında mevcut navigator.mediaSession.metadata değerinin kullanılacağını unutmayın. Bu nedenle, medya bildirimlerinde her zaman alakalı bilgiler göstermek için bu sayfayı güncellemeniz gerekir.

Önceki parça / sonraki parça

Web uygulamanızda şarkı listesi varsa kullanıcının "Önceki Parça" ve "Sonraki Parça" simgelerini kullanarak doğrudan medya bildiriminden şarkı listenizde gezinmesine izin verebilirsiniz.

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

Medya işlemi işleyicilerinin devam edeceğini unutmayın. Bu, etkinlik dinleyici kalıbına çok benzer. Tek fark, bir etkinliğin işlenmesi durumunda tarayıcının varsayılan davranışı durdurması ve bunu web uygulamanızın medya işlemini desteklediğinin bir sinyali olarak kullanmasıdır. Bu nedenle, uygun işlem işleyiciyi ayarlamadığınız sürece medya işlemi kontrolleri gösterilmez.

Bir medya işlemi işleyicisinin ayarını kaldırmak, null değerine atama işlemi kadar kolaydır.

Geri / ileri sarma

Media Session API, atlanan süreyi kontrol etmek istiyorsanız "Geri Sar" ve "İleri Sar" medya bildirim simgelerini göstermenize olanak tanır.

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

Oynat / duraklat

"Oynat/Duraklat" simgesi medya bildirimde her zaman gösterilir ve ilgili etkinlikler tarayıcı tarafından otomatik olarak yönetilir. Varsayılan davranış herhangi bir nedenle işe yaramazsa "Oynat" ve "Duraklat" medya etkinliklerini yine de işleyebilirsiniz.

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...
});

Her yerde bildirimler

Media Session API'nin en iyi özelliklerinden biri, medya meta verilerinin ve denetimlerinin yalnızca bildirim tepsisinde görünmemesidir. Medya bildirimi, eşlenen tüm giyilebilir cihazlarla otomatik olarak senkronize edilir. Ayrıca kilit ekranlarında da gösterilir.

Kilit Ekranı
Kilit Ekranı - Fotoğraf Michael Alø-Nielsen / CC BY 2.0
Wear Bildirimi
Wear Bildirimi

Çevrimdışı oynarken sorun yaşamamak için

Şu anda ne düşündüğünüzü biliyorum. Hizmet çalışanı imdadınıza yetişiyor!

Doğru, ancak öncelikle bu yapılacaklar listesinde yer alan tüm öğelerin işaretlendiğinden emin olmanız gerekir:

  • Tüm medya ve poster dosyaları, uygun Cache-Control HTTP üst bilgisiyle yayınlanır. Bu, tarayıcının daha önce getirilen kaynakları önbelleğe almasına ve yeniden kullanmasına olanak tanır. Önbelleğe alma kontrol listesine bakın.
  • Tüm medya ve poster dosyalarının Allow-Control-Allow-Origin: * HTTP üst bilgisiyle yayınlandığından emin olun. Bu, üçüncü taraf web uygulamalarının web sunucunuzdan HTTP yanıtları almasına ve kullanmasına olanak tanır.

Hizmet çalışanı önbelleğe alma stratejisi

Medya dosyaları için, Jake Archibald'ın gösterdiği gibi basit bir "Önbellek, ağa geri dönme" stratejisi öneririm.

Poster için ise biraz daha ayrıntılı bir yaklaşım seçerdim:

  • If posteri zaten önbellekte. Önbellekten sunun.
  • Else posteri ağdan getiriyor
    • If getirme işlemi başarılıysa ağ posterini önbelleğe ekleyin ve yayınlayın
    • Else Yedek posteri önbellekten sunma

Bu şekilde, tarayıcı bildirimleri alamasa bile medya bildirimleri her zaman güzel bir poster simgesine sahip olur. Bunu nasıl uygulayabileceğiniz aşağıda açıklanmıştır:

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

Kullanıcının önbelleği kontrol etmesine izin ver

Kullanıcı web uygulamanızdan içerik tüketirken medya ve poster dosyaları cihazında çok fazla yer kaplıyor olabilir. Önbelleğin ne kadar kullanıldığını göstermek ve kullanıcılara önbelleği temizlemelerine olanak tanımak sizin sorumluluğunuzdadır. Neyse ki bunu Cache API ile yapmak oldukça kolay.

// 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); });

Uygulama notları

  • Android için Chrome, medya bildirimlerini göstermek için yalnızca medya dosyası süresi en az 5 saniye olduğunda "tam" ses odağını ister.
  • Bildirim posterleri, blob URL'lerini ve veri URL'lerini destekler.
  • Bir poster tanımlanmamışsa ve istediğiniz boyutta bir simge resmi varsa medya bildirimleri bunu kullanır.
  • Android için Chrome'da bildirim posteri boyutu 512x512'tür. Düşük özellikli cihazlar için 256x256 değeri geçerlidir.
  • audio.src = '' ile medya bildirimlerini kapatabilirsiniz.
  • Web Audio API, Android Ses Odağı'nı geçmiş nedenlerden dolayı istemediğinden, API'nin Media Session API ile çalışmasını sağlamanın tek yolu <audio> öğesini Web Audio API'nin giriş kaynağı olarak bağlamaktır. Önerilen Web AudioFocus API'nin yakın gelecekte bu durumu iyileştireceğini umuyoruz.
  • Medya Oturumu çağrıları, yalnızca medya kaynağıyla aynı çerçeveden gelen medya bildirimlerini etkiler. Aşağıdaki snippet'i inceleyin.
<iframe id="iframe">
  <audio>...</audio>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Destek

Bu makalenin yazıldığı sırada Media Session API'yi destekleyen tek platform Android için Chrome'dur. Tarayıcı uygulama durumuyla ilgili en güncel bilgileri Chrome Platform Durumu'nda bulabilirsiniz.

Örnekler ve demolar

Blender Foundation ve Jan Morgenstern'ın çalışmalarının yer aldığı resmi Chrome Medya Oturumu örneklerine göz atın.

Kaynaklar

Medya Oturumu Spesifikasyonu: wicg.github.io/mediasession

Spesifikasyon sorunları: github.com/WICG/mediasession/issues

Chrome Hataları: crbug.com