其實這篇文章也可以叫如何監聽並上傳使用者觀看的視訊時長。最近有需求做到,監聽使用者播放視訊的動作並上報播放事件,需要上報的是使用者觀看視訊的時長。這裡就有幾種情況了,一是使用者點選播放,然後直到視訊播放完畢,觸發video.ended事件,此時上報使用者的觀看時長,這個比較好處理。第二種情況是使用者點選播放後離開或重新整理頁面,在使用者離開前需要把事件上報,這裡主要會觸發window.onunload事件,看起來可以做到,其實有坑。
由於公司採用的技術方案是.m3u8(hls),而這種格式目前PC瀏覽器只有Safari才支援,chrome、Firefox或移動端安卓其他的大部分瀏覽器都不支援播放這種格式。因此,用原生video標籤的方案是不行了,得找一些外掛或播放器。最終選擇了DPlayer這個播放器,一個比較不錯的開源播放器,簡單易用。
說回正事,上報使用者觀看視訊的時長。
重要更新
經評論區 @音客 指點,使用Navigator.sendBeacon()將會更好的支援上報事件,這是一個專門處理資料埋點的api,但是使用時請注意瀏覽器相容性問題,目前(2018.06)移動端Android 5以上支援,ios需要11.3版本以上,具體請看CanIUse。
本篇使用new Image()
方法可以作為sendBeacon()
的不相容時的降級處理方法,更多請參考:使用 navigator.sendBeacon() 上報資料。
目標
使用者觀看視訊結束時上報觀看時長。
準備工作
const dp = new DPlayer({
container: document.getElementById(`dplayer`),
screenshot: true,
video: {
url: `xx.m3u8`,
pic: `xx.jpg`,
type: `customHls`,
customType: {
`customHls`: function (video, player) {
const hls = new Hls();
hls.loadSource(video.src);
hls.attachMedia(video);
}
}
}
});
// 是否播放的標記
var isPlay = false;
dp.on(`play`, function () {
console.log(`player start`);
isPlay = true;
});
複製程式碼
第一種常規的情況,使用者完整地看完了視訊。
dp.on(`ended`, function () {
console.log(`player ended`);
sendVideoPlayEvent(); //上報事件方法
isPlay = false;
});
複製程式碼
第二種情況,關閉瀏覽器或重新整理頁面
使用者關閉頁面或重新整理瀏覽器時會觸發onunload
事件,使用者開始觀看視訊到離開頁面,這一段時間就是使用者觀看視訊的時長。
window.onunload = function () {
sendVideoPlayEvent();
isPlay = false;
console.log(`onunload`);
};
複製程式碼
坑點
經測試發現,sendVideoPlayEvent時,如果採用ajax,無論是post或get方式,將無效。這裡的原因可能是頁面關閉太快,ajax無法執行。
查了很多解決方案,最後發現,需要通過巧妙的方法來發起這個事件。就是利用img標籤的src屬性,將img插入到body中時會發起一個請求來獲取資源,而服務端可以對這個路由進行處理,最後發現這種方法是可行的,成功地在onunload中上報事件了。
function sendVideoPlayEvent() {
if (isPlay) {
var videoId = getQueryString(`videoId`);
var duration = dp.video.currentTime; // 視訊播放時間可以直接通過currentTime獲得
var img = new Image();
img.style.display = `none`;
img.src = `/api/video/play?duration=${duration}&videoId=${videoId}`; // 服務端處理介面
document.body.appendChild(img);
}
}
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
複製程式碼
總結
- 後來想想,script標籤的src應該也可以實現,沒去驗證。
- 坑總是很多,多動腦筋。。。
- 思路有點侷限,應該還有其他解決方案,歡迎建議。