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 API Phiên nội dung nghe nhìn hoàn toàn mới, giờ đây, bạn có thể tuỳ chỉnh thông báo về nội dung nghe nhìn bằng cách cung cấp siêu dữ liệu cho nội dung nghe nhìn mà ứng dụng web của bạn đang phát. API 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ư các sự kiện tìm kiếm hoặc theo dõi việc thay đổi có thể đến từ các thông báo hoặc các phím phương tiện. Bạn có muốn thực hiện ngay không? Hãy dùng thử mẫu Phiên nội dung nghe nhìn chính thức.

Media Session API được hỗ trợ trong Chrome 57 (thử nghiệm vào tháng 2 năm 2017, ổn định vào tháng 3 năm 2017).

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

Cho tôi biết nội dung tôi muốn

Bạn đã biết về Media Session API và chỉ đơn giản là quay lại để sao chép và dán mà không thấy ngại một số mã nguyên mẫu? Tôi sẽ tìm thấy thông tin cho bạn.

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 về đoạn mã

Chơi thôi 🎷

Thêm một phần tử <audio> đơn giản vào trang web của bạn và chỉ định một số nguồn nội dung nghe nhìn để trình duyệt có thể chọn nguồn hoạt động tốt 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 dành cho Android. Điều này có 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 một 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 nút trước khi ứng dụng web của bạn thực sự tạo ra nhiễu.

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ần 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 tính năng này cũng có thể giúp quá trình phát mượt mà 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 của bạn đang phát âm thanh, bạn có thể thấy một thông báo nội dung nghe nhì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 có thể tìm thấy.

Không có phiên nội dung nghe nhìn
Không có phiên phát nội dung nghe nhìn
Có phiên nội dung nghe nhìn
Có phiên phát nội dung nghe nhìn

Thiết lập siêu dữ liệu

Hãy xem cách tuỳ chỉnh thông báo về nội dung đa phương tiện này bằng cách đặt một số siêu dữ liệu của phiên phát nội dung đa phương tiện như tiêu đề, nghệ sĩ, tên đĩa nhạc và hình minh hoạ bằng API Phiên nội dung đa phương tiện.

// 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 đa phương tiệ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 đầu phát bất kỳ nội dung nào. Đây là lý do bạn cần cập nhật thông báo này để đảm bảo bạn luôn hiển thị thông tin liên quan trong thông báo về 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, thì bạn nên cho phép người dùng di chuyển trong danh sách phát ngay trong thông báo về nội dung nghe nhìn với 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 các trình xử lý thao tác đối với nội dung đa phương tiện sẽ vẫn tồn tại. Mẫ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 đây làm tín hiệu cho biết ứng dụng web của bạn hỗ trợ hành động đối với nội dung đa phương tiện. Do đó, các tuỳ chọn điều khiển thao tác đối với nội dung đa phương tiện sẽ không xuất hiện trừ phi bạn đặt trình xử lý thao tác thích hợp.

Nhân tiện, việc huỷ thiết lập một trình xử lý hành động đối với nội dung đa phương tiện cũng dễ dàng như việc chỉ định trình xử lý đó cho null.

Tua lại / Tua đi

Media Session API cho phép bạn hiển thị các biểu tượng thông báo nội dung nghe nhìn "Tua lại" và "Tua đi" nếu muốn kiểm soát khoảng thời gian bị 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 hiển thị trong thông báo về nội dung nghe nhìn và các sự kiện liên quan sẽ được trình duyệt xử lý tự động. 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 nội dung nghe nhìn "Phát" và "Tạm dừng".

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ị của 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 về nội dung nghe nhìn sẽ được đồng bộ hoá tự động với mọi thiết bị đeo đã ghép nối. Và nó cũng hiển thị 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 trên Wear

Phát nội dung dễ chịu khi không có mạng

Tôi biết bây giờ bạn đang nghĩ gì. Nhân viên dịch vụ sẽ cứu cánh!

Đúng, nhưng trước hết, bạn muốn đảm bảo tất cả các mục trong danh sách kiểm tra này đều được chọn:

  • Tất cả các tệp nội dung nghe nhìn và hình minh hoạ đều được phân phát cù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 lưu vào bộ nhớ đệm.
  • Đảm bảo tất cả các tệp nội dung nghe nhìn và hình minh hoạ đều được phân phát cùng tiêu đề HTTP Allow-Control-Allow-Origin: *. Điều này sẽ cho phép các ứng dụng web 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 nội dung nghe nhìn, bạn nên sử dụng chiến lược "Cache, back to network" đơn giản, như minh hoạ của Jake Archibald.

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:

  • If hình minh hoạ đã 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, hãy 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 các hình minh hoạ đó. Dưới đây là cách bạn có thể triển khai việ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ị. Bạn có trách nhiệm cho biết mức sử dụng bộ nhớ đệm và cho phép người dùng xoá bộ nhớ đệm đó. Rất may cho chúng tôi là việc này khá dễ dàng nhờ 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); });

Những lưu ý khi triển khai

  • Chrome dành cho Android yêu cầu quyền phát âm thanh "đầy đủ" để chỉ hiển thị thông báo về nội dung nghe nhìn khi thời lượng của tệp đa phương tiện là ít nhất 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ạ nào và có một hình ảnh biểu tượng ở kích thước mong muốn, thì thông báo về nội dung nghe nhìn sẽ sử dụng hình minh hoạ đó.
  • Kích thước hình minh hoạ thông báo trong Chrome dành cho Android là 512x512. Đối với các thiết bị cấp thấp, giá trị này là 256x256.
  • Loại bỏ thông báo về 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 để 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 API Web AudioFocus mà chúng tôi đề xuất sẽ giúp cải thiện tình hình trong tương lai gần.
  • Các lệnh gọi Phiên phát nội dung đa phương tiện sẽ chỉ ảnh hưởng đến các thông báo về nội dung nghe nhìn nếu các lệnh gọi này đế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ã dưới đây.
<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 này, Chrome dành 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 của trình duyệt trên trang Trạng thái nền tảng Chrome.

Đoạn nhạc và bản minh hoạ

Xem mẫu Phiên truyền thông chính thức của chúng tôi trên Chrome với sự góp mặt của Blender Foundationtác phẩm của Jan Morgenstern.

Tài nguyên

Thông số phiên phát nội dung đa phương tiện: wicg.github.io/mediasession

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

Lỗi Chrome: crbug.com