Tuỳ chỉnh thông báo về nội dung nghe nhìn và xử lý danh sách phát

François Beaufort
François Beaufort

Với Media Session API hoàn toàn mới, giờ đây, bạn có thể tuỳ chỉnh thông báo nội dung đa phương tiện bằng cách cung cấp siêu dữ liệu cho nội dung đa phương tiện mà ứng dụng web của bạn đang phát. Lớp này cũng cho phép bạn xử lý các sự kiện liên quan đến nội dung nghe nhìn, chẳng hạn như tua hoặc thay đổi bản nhạc có thể đến từ thông báo hoặc phím đa phương tiện. Bạn có muốn thực hiện ngay không? Hãy thử các mẫu Phiên phát nội dung nghe nhìn chính thức.

Media Session API được hỗ trợ trong Chrome 57 (phiên bản thử nghiệm vào tháng 2 năm 2017, phiên bản chính thức vào tháng 3 năm 2017).

Tóm tắt về Phiên phát nội dung đa phương tiện;
Ảnh của Michael Alø-Nielsen / CC BY 2.0

Gimme what I want

Bạn đã biết về API Phiên phát nội dung nghe nhìn và chỉ cần quay lại để sao chép và dán một số mã nguyên mẫu mà không hề xấu hổ? Vậy thì đây là kết quả.

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

Tìm hiểu mã

Chơi thôi 🎷

Thêm một phần tử <audio> đơn giản vào trang web và chỉ định một số nguồn nội dung đa phương tiện để trình duyệt có thể chọn nguồn phù hợp nhất.

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

Như bạn có thể biết, autoplay bị tắt đối với các phần tử âm thanh trên Chrome cho Android, nghĩa là chúng ta phải sử dụng phương thức play() của phần tử âm thanh. Phương thức này phải được kích hoạt bằng cử chỉ của người dùng, chẳng hạn như thao tác chạm hoặc nhấp chuột. Điều đó có nghĩa là theo dõi các sự kiện pointerup, clicktouchend. Nói cách khác, người dùng phải nhấp vào một nút trước khi ứng dụng web của bạn thực sự có thể phát ra âm thanh.

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

Nếu không muốn phát âm thanh ngay sau lượt tương tác đầu tiên, bạn nên sử dụng phương thức load() của phần tử âm thanh. Đây là một cách để trình duyệt theo dõi xem người dùng có tương tác với phần tử hay không. Xin lưu ý rằng việc này cũng có thể giúp quá trình phát diễn ra suôn sẻ vì nội dung sẽ được tải sẵ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) });
});

Tuỳ chỉnh thông báo

Khi ứng dụng web đang phát âm thanh, bạn có thể thấy thông báo đa phương tiện trong khay thông báo. Trên Android, Chrome cố gắng hết sức để hiển thị thông tin phù hợp bằng cách sử dụng tiêu đề của tài liệu và hình ảnh biểu tượng lớn nhất mà Chrome có thể tìm thấy.

Không có phiên phát nội dung đa phương tiện
Không có phiên phát nội dung nghe nhìn
Có phiên phát nội dung đa phương tiện
Có phiên phát nội dung đa phương tiện

Đặt siêu dữ liệu

Hãy xem cách tuỳ chỉnh thông báo nội dung nghe nhìn này bằng cách đặt một số siêu dữ liệu phiên nội dung nghe nhìn như tiêu đề, nghệ sĩ, tên album và hình minh hoạ bằng 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' },
    ]
    });
}

Sau khi phát xong, bạn không cần phải "giải phóng" phiên phát nội dung nghe nhìn vì thông báo sẽ tự động biến mất. Xin lưu ý rằng navigator.mediaSession.metadata hiện tại sẽ được sử dụng khi bất kỳ quá trình phát nào bắt đầu. Đó là lý do bạn cần cập nhật thông tin này để đảm bảo luôn hiển thị thông tin liên quan trong thông báo nội dung nghe nhìn.

Bản nhạc trước / bản nhạc tiếp theo

Nếu ứng dụng web của bạn cung cấp danh sách phát, bạn nên cho phép người dùng điều hướng qua danh sách phát ngay trong thông báo nội dung nghe nhìn bằng một số biểu tượng "Bản nhạc trước" và "Bản nhạc tiếp theo".

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

Xin lưu ý rằng trình xử lý hành động đa phương tiện sẽ vẫn tồn tại. Điều này rất giống với mẫu trình nghe sự kiện, ngoại trừ việc xử lý một sự kiện có nghĩa là trình duyệt sẽ ngừng thực hiện mọi hành vi mặc định và sử dụng điều này làm tín hiệu cho biết ứng dụng web của bạn hỗ trợ thao tác đa phương tiện. Do đó, các chế độ điều khiển hành động đa phương tiện sẽ không hiển thị trừ khi bạn đặt trình xử lý hành động thích hợp.

Nhân tiện, việc huỷ thiết lập trình xử lý hành động đa phương tiện cũng dễ dàng như việc gán trình xử lý đó cho null.

Tua lại / tua đi

Media Session API cho phép bạn hiển thị biểu tượng thông báo nội dung nghe nhìn "Tìm ngược" và "Tìm tiến" nếu bạn muốn kiểm soát khoảng thời gian bỏ qua.

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

Phát / tạm dừng

Biểu tượng "Phát/Tạm dừng" luôn xuất hiện trong thông báo nội dung nghe nhìn và trình duyệt sẽ tự động xử lý các sự kiện liên quan. Nếu vì lý do nào đó mà hành vi mặc định không hoạt động, bạn vẫn có thể xử lý các sự kiện phát và tạm dừng nội dung nghe nhìn.

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

Thông báo ở mọi nơi

Điều thú vị về Media Session API là khay thông báo không phải là nơi duy nhất hiển thị siêu dữ liệu và các chế độ điều khiển nội dung nghe nhìn. Thông báo nội dung nghe nhìn sẽ tự động đồng bộ hoá với mọi thiết bị đeo được ghép nối. Đồng thời, thông báo này cũng xuất hiện trên màn hình khoá.

Khóa màn hình
Màn hình khoá – Ảnh của Michael Alø-Nielsen / CC BY 2.0
Thông báo trên Wear
Thông báo Wear

Phát tốt khi không có mạng

Tôi biết bạn đang nghĩ gì. Trình chạy dịch vụ sẽ giúp bạn giải quyết vấn đề này!

Đúng vậy, nhưng trước tiên, bạn cần đảm bảo đã đánh dấu tất cả các mục trong danh sách kiểm tra này:

  • Tất cả tệp nội dung nghe nhìn và hình minh hoạ đều được phân phát bằng tiêu đề HTTP Cache-Control thích hợp. Điều này sẽ cho phép trình duyệt lưu vào bộ nhớ đệm và sử dụng lại các tài nguyên đã tìm nạp trước đó. Xem Danh sách kiểm tra về việc lưu vào bộ nhớ đệm.
  • Đảm bảo tất cả tệp nội dung nghe nhìn và hình minh hoạ đều được phân phát bằng tiêu đề HTTP Allow-Control-Allow-Origin: *. Điều này sẽ cho phép các ứng dụng web của bên thứ ba tìm nạp và sử dụng phản hồi HTTP từ máy chủ web của bạn.

Chiến lược lưu vào bộ nhớ đệm của trình chạy dịch vụ

Đối với tệp phương tiện, bạn nên sử dụng chiến lược "Bộ nhớ đệm, quay lại mạng" đơn giản như Jake Archibald đã minh hoạ.

Tuy nhiên, đối với hình minh hoạ, tôi sẽ cụ thể hơn một chút và chọn phương pháp bên dưới:

  • Hình minh hoạ If đã có trong bộ nhớ đệm, hãy phân phát hình minh hoạ đó từ bộ nhớ đệm
  • Else tìm nạp hình minh hoạ từ mạng
    • Tìm nạp If thành công, thêm hình minh hoạ mạng vào bộ nhớ đệm và phân phát hình minh hoạ đó
    • Else phân phát hình minh hoạ dự phòng từ bộ nhớ đệm

Bằng cách đó, thông báo về nội dung nghe nhìn sẽ luôn có biểu tượng hình minh hoạ đẹp mắt ngay cả khi trình duyệt không thể tìm nạp. Sau đây là cách bạn có thể triển khai phương thức này:

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

Cho phép người dùng kiểm soát bộ nhớ đệm

Khi người dùng sử dụng nội dung từ ứng dụng web của bạn, các tệp nội dung nghe nhìn và hình minh hoạ có thể chiếm nhiều dung lượng trên thiết bị của họ. Bạn có trách nhiệm cho biết lượng bộ nhớ đệm được sử dụng và cho phép người dùng xoá bộ nhớ đệm. Rất may là chúng ta có thể làm việc này khá dễ dàng với API bộ nhớ đệm.

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

Ghi chú triển khai

  • Chrome cho Android yêu cầu quyền phát âm thanh "đầy đủ" để hiển thị thông báo đa phương tiện chỉ khi thời lượng tệp đa phương tiện ít nhất là 5 giây.
  • Hình minh hoạ thông báo hỗ trợ URL blob và URL dữ liệu.
  • Nếu không xác định được hình minh hoạ và có hình ảnh biểu tượng ở kích thước mong muốn, thì thông báo nội dung nghe nhìn sẽ sử dụng hình ảnh đó.
  • Kích thước hình minh hoạ thông báo trong Chrome dành cho Android là 512x512. Đối với thiết bị cấp thấp, giá trị này là 256x256.
  • Đóng thông báo nội dung nghe nhìn bằng audio.src = ''.
  • Web Audio API không yêu cầu Android Audio Focus vì lý do trước đây, nên cách duy nhất để làm cho API này hoạt động với Media Session API là kết nối một phần tử <audio> làm nguồn đầu vào cho Web Audio API. Hy vọng rằng Web AudioFocus API được đề xuất sẽ cải thiện tình hình trong tương lai gần.
  • Lệnh gọi Phiên phát nội dung nghe nhìn sẽ chỉ ảnh hưởng đến thông báo nội dung nghe nhìn nếu các lệnh gọi đó đến từ cùng một khung với tài nguyên nội dung nghe nhìn. Hãy xem đoạn mã bên dưới.
<iframe id="iframe">
  <audio>...</audio>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Hỗ trợ

Tại thời điểm viết bài, Chrome cho Android là nền tảng duy nhất hỗ trợ Media Session API. Bạn có thể xem thêm thông tin mới nhất về trạng thái triển khai trình duyệt trên trang Trạng thái của nền tảng Chrome.

Mẫu và bản minh hoạ

Hãy xem các mẫu Phiên phát nội dung nghe nhìn chính thức của Chrome có Blender Foundationcông việc của Jan Morgenstern.

Tài nguyên

Thông số kỹ thuật của phiên phát nội dung nghe nhìn: wicg.github.io/mediasession

Vấn đề về thông số kỹ thuật: github.com/WICG/mediasession/issues

Lỗi Chrome: crbug.com