import Artplayer from 'https://dpangzi.com/library/artplayer/artplayer.mjs';
import artplayerPluginDanmuku from 'https://dpangzi.com/library/artplayer-plugin-danmuku/artplayer-plugin-danmuku.mjs'
import Hls from 'https://dpangzi.com/library/hls.js/hls.mjs';
/**
* Video Module
* Handles video playback on the watch page
*/
class VideoPlayer {
constructor() {
this.containerSelector = '#video-player';
this.dataAttribute = 'data-video-config';
this.hasPlayed = false;
}
init() {
const container = document.querySelector(this.containerSelector);
if (!container) return;
// Check if player already initialized to prevent duplicates if called multiple times incorrectly
if (container.classList.contains('artplayer-app')) {
// Already initialized
return;
}
const configStr = container.getAttribute(this.dataAttribute);
if (!configStr) return;
try {
const config = JSON.parse(configStr);
this.setupPlayer(container, config);
} catch (e) {
console.error('Failed to parse video config', e);
}
}
setupPlayer(container, config) {
const { id, url, cover, title } = config;
const videoId = id;
const art = new Artplayer({
container: container,
url: url,
type: 'm3u8',
poster: cover,
title: title,
volume: 0.5,
isLive: false,
muted: false,
autoplay: false,
pip: true,
autoSize: true,
autoMini: true,
screenshot: true,
setting: true,
loop: false,
flip: true,
playbackRate: true,
aspectRatio: true,
fullscreen: true,
fullscreenWeb: true,
miniProgressBar: true,
mutex: true,
backdrop: true,
playsInline: true,
autoPlayback: true,
airplay: true,
theme: '#2563eb', // Matches primary color
customType: {
m3u8: (video, url, art) => {
if (Hls.isSupported()) {
if (art.hls) art.hls.destroy();
const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);
art.hls = hls;
art.on('destroy', () => hls.destroy());
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = url;
} else {
art.notice.show = 'Unsupported playback format: m3u8';
}
}
},
plugins: [
artplayerPluginDanmuku({
danmuku: async function () {
try {
const response = await fetch(`/history/danmaku/v2/${videoId}`);
return await response.json();
} catch (e) {
console.error('Failed to load danmaku', e);
return [];
}
},
async beforeEmit(danmaku) {
if (!danmaku.text || danmaku.text.trim() === "") {
return false;
}
danmaku["id"] = videoId;
try {
await fetch(`/send/danmaku/v2`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(danmaku)
});
const countEl = document.querySelector('.video-info__meta-item[title="弹幕数"] span');
if (countEl) {
const text = countEl.innerText;
const match = text.match(/(\d+)\s*弹幕/);
if (match) {
const count = parseInt(match[1]) + 1;
countEl.innerText = `${count} 弹幕`;
}
}
return true;
} catch (e) {
console.error('Failed to send danmaku', e);
return false;
}
}
})
],
});
// Handle custom events or analytics here if needed
art.on('play', async () => {
if (!this.hasPlayed) {
this.hasPlayed = true;
await fetch(`/play/${videoId}`, { method: 'PATCH' });
}
});
}
}
export class Video {
constructor() {
const videoPlayer = new VideoPlayer();
videoPlayer.init();
}
}
评论加载中...