اعلان‌های رسانه را سفارشی کنید و لیست‌های پخش را مدیریت کنید

فرانسوا بوفور
François Beaufort

با نام تجاری جدید Media Session API ، اکنون می توانید اعلان های رسانه را با ارائه ابرداده برای رسانه ای که برنامه وب شما پخش می کند، سفارشی کنید . همچنین به شما امکان می دهد رویدادهای مرتبط با رسانه مانند جستجو یا ردیابی تغییر را که ممکن است از اعلان ها یا کلیدهای رسانه باشد، مدیریت کنید . برانگیخته؟ نمونه های رسمی جلسه رسانه را امتحان کنید.

Media Session API در Chrome 57 (بتا در فوریه 2017، در مارس 2017 پایدار) پشتیبانی می‌شود.

جلسه رسانه TL;DR;
عکس از Michael Alø-Nielsen / CC BY 2.0

آنچه را که میخواهم بده

شما قبلاً در مورد Media Session API می‌دانید و به سادگی برای کپی و چسباندن برخی از کدهای دیگ بخار بازمی‌گردید؟ پس اینجاست.

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

وارد کد شوید

بیا بازی کنیم 🎷

یک عنصر <audio> ساده را به صفحه وب خود اضافه کنید و چندین منبع رسانه را اختصاص دهید تا مرورگر بتواند انتخاب کند که کدام یک بهترین کارایی را دارد.

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

همانطور که ممکن است بدانید، autoplay برای عناصر صوتی در Chrome برای Android غیرفعال است، به این معنی که باید از روش play() عنصر صوتی استفاده کنیم. این روش باید با یک حرکت کاربر مانند لمس یا کلیک ماوس فعال شود. این به معنای گوش دادن به رویدادهای pointerup ، click و touchend . به عبارت دیگر، کاربر باید قبل از اینکه برنامه وب شما واقعاً نویز ایجاد کند، روی یک دکمه کلیک کند.

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

اگر نمی خواهید صدا را بلافاصله پس از اولین تعامل پخش کنید، توصیه می کنم از متد load() عنصر صوتی استفاده کنید. این یکی از راه‌های مرورگر برای پیگیری این است که آیا کاربر با عنصر تعامل داشته است یا خیر. توجه داشته باشید که ممکن است به روان شدن پخش نیز کمک کند زیرا محتوا قبلاً بارگذاری شده است.

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

اعلان را سفارشی کنید

وقتی برنامه وب شما در حال پخش صدا است، می‌توانید از قبل یک اعلان رسانه را در سینی اعلان ببینید. در اندروید، کروم تمام تلاش خود را می کند تا با استفاده از عنوان سند و بزرگترین تصویر نمادی که می تواند پیدا کند، اطلاعات مناسب را نشان دهد.

بدون جلسه رسانه ای
بدون جلسه رسانه ای
با جلسه رسانه ای
با جلسه رسانه ای

متادیتا را تنظیم کنید

بیایید ببینیم که چگونه می‌توان این اعلان رسانه را با تنظیم برخی فراداده‌های جلسه رسانه مانند عنوان، هنرمند، نام آلبوم و اثر هنری با 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' },
    ]
    });
}

پس از اتمام پخش، لازم نیست جلسه رسانه را "آزاد کنید" زیرا اعلان به طور خودکار ناپدید می شود. به خاطر داشته باشید که navigator.mediaSession.metadata کنونی هنگام شروع پخش مورد استفاده قرار خواهد گرفت. به همین دلیل است که باید آن را به روز کنید تا مطمئن شوید که همیشه اطلاعات مرتبط را در اعلان رسانه نشان می دهید.

آهنگ قبلی / آهنگ بعدی

اگر برنامه وب شما یک لیست پخش ارائه می دهد، ممکن است بخواهید به کاربر اجازه دهید تا مستقیماً از طریق اعلان رسانه با برخی از نمادهای «تراک قبلی» و «آهنگ بعدی» در لیست پخش شما پیمایش کند.

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

توجه داشته باشید که کنترل‌کننده‌های کنش رسانه‌ای همچنان ادامه خواهند داشت. این بسیار شبیه الگوی شنونده رویداد است با این تفاوت که مدیریت یک رویداد به این معنی است که مرورگر انجام هر گونه رفتار پیش فرض را متوقف می کند و از آن به عنوان سیگنالی استفاده می کند که برنامه وب شما از عملکرد رسانه پشتیبانی می کند. بنابراین، کنترل‌های کنش رسانه‌ای نشان داده نمی‌شوند مگر اینکه کنترل‌کننده عملکرد مناسب را تنظیم کنید.

به هر حال، لغو تنظیم یک کنترل کننده اکشن رسانه به آسانی اختصاص دادن آن به null است.

به دنبال عقب / به دنبال جلو

Media Session API به شما این امکان را می‌دهد که نمادهای اعلان رسانه "Seek Backward" و "Seek Forward" را نشان دهید، اگر می‌خواهید مدت زمان پرش را کنترل کنید.

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

پخش توقف

نماد «پخش/مکث» همیشه در اعلان رسانه نشان داده می‌شود و رویدادهای مرتبط به‌طور خودکار توسط مرورگر مدیریت می‌شوند. اگر به دلایلی رفتار پیش‌فرض درست نشد، همچنان می‌توانید رویدادهای رسانه‌ای «پخش» و «مکث» را مدیریت کنید.

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

اعلان ها در همه جا

نکته جالب در مورد Media Session API این است که سینی اعلان تنها مکانی نیست که متادیتا و کنترل های رسانه قابل مشاهده هستند. اعلان رسانه به صورت خودکار با هر دستگاه پوشیدنی جفت شده همگام سازی می شود. و همچنین در صفحه های قفل نمایش داده می شود.

صفحه قفل
صفحه قفل - عکس Michael Alø-Nielsen / CC BY 2.0
اعلان پوشیدن
اعلان پوشیدن

آن را به خوبی آفلاین بازی کنید

میدونم الان به چی فکر میکنی کارگر خدمات به نجات!

درست است، اما قبل از هر چیز، شما می خواهید مطمئن شوید که همه موارد در این چک لیست بررسی شده اند :

  • همه فایل‌های رسانه و آثار هنری با هدر HTTP Cache-Control مناسب ارائه می‌شوند. این به مرورگر اجازه می‌دهد تا منابعی که قبلاً واکشی شده‌اند را ذخیره کرده و مجدداً استفاده کند. چک لیست ذخیره سازی را ببینید.
  • مطمئن شوید که همه فایل‌های رسانه و آثار هنری با Allow-Control-Allow-Origin: * هدر HTTP ارائه می‌شوند. این به برنامه های وب شخص ثالث اجازه می دهد تا پاسخ های HTTP را از سرور وب شما دریافت و مصرف کنند.

استراتژی ذخیره سازی کارگر خدماتی

در مورد فایل های رسانه ای، من یک استراتژی ساده " Cache، بازگشت به شبکه " را توصیه می کنم که توسط Jake Archibald نشان داده شده است.

با این حال، برای کارهای هنری، من کمی دقیق تر هستم و روش زیر را انتخاب می کنم:

  • If اثر هنری از قبل در حافظه پنهان است، آن را از حافظه پنهان سرو کنید
  • Else آثار هنری را از شبکه واکشی کنید
    • If واکشی موفقیت آمیز بود، آثار هنری شبکه را به حافظه پنهان اضافه کنید و آن را سرو کنید
    • Else اثر هنری بازگشتی را از حافظه پنهان ارائه دهید

به این ترتیب، اعلان‌های رسانه همیشه نماد آثار هنری خوبی خواهند داشت، حتی زمانی که مرورگر نتواند آنها را واکشی کند. در اینجا نحوه اجرای این کار آمده است:

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

اجازه دهید کاربر حافظه پنهان را کنترل کند

از آنجایی که کاربر محتوای برنامه وب شما را مصرف می کند، فایل های رسانه و آثار هنری ممکن است فضای زیادی را در دستگاه او اشغال کنند. این مسئولیت شماست که نشان دهید چه مقدار حافظه پنهان استفاده شده است و به کاربران امکان پاک کردن آن را بدهید . خوشبختانه برای ما، انجام این کار با 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); });

یادداشت های اجرایی

  • Chrome for Android فوکوس صوتی «کامل» را درخواست می‌کند تا اعلان‌های رسانه را فقط زمانی نشان دهد که مدت زمان فایل رسانه حداقل 5 ثانیه باشد.
  • آثار هنری اعلان از URLهای حباب و URLهای داده پشتیبانی می کند.
  • اگر هیچ اثر هنری تعریف نشده باشد و یک تصویر نماد در اندازه دلخواه وجود داشته باشد، اعلان‌های رسانه از آن استفاده می‌کنند.
  • اندازه اثر هنری اعلان در Chrome برای Android 512x512 است. برای دستگاه های ارزان قیمت ، 256x256 است.
  • اعلان‌های رسانه را با audio.src = '' رد کنید.
  • از آنجایی که Web Audio API به دلایل تاریخی از Android Audio Focus درخواست نمی‌کند، تنها راه کارکرد آن با Media Session API این است که یک عنصر <audio> را به عنوان منبع ورودی به Web Audio API متصل کنید. امیدواریم که Web AudioFocus API پیشنهادی وضعیت را در آینده نزدیک بهبود بخشد.
  • تماس‌های Media Session تنها در صورتی بر اعلان‌های رسانه تأثیر می‌گذارند که از همان قاب منبع رسانه باشند. قطعه زیر را ببینید.
<iframe id="iframe">
  <audio>...</audio>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

حمایت کردن

در زمان نگارش این مقاله، Chrome for Android تنها پلتفرمی است که از Media Session API پشتیبانی می‌کند. اطلاعات به‌روزتر درباره وضعیت اجرای مرورگر را می‌توانید در وضعیت پلتفرم Chrome پیدا کنید.

نمونه ها و دموها

نمونه‌های جلسه رسمی Chrome Media Session ما را که شامل کارهای Blender Foundation و Jan Morgenstern است، بررسی کنید.

منابع

مشخصات جلسه رسانه: wicg.github.io/mediasession

مشکلات مشخصات: github.com/WICG/mediasession/issues

اشکالات کروم: crbug.com