import Artplayer from 'artplayer';
import { BaseVideoPlayer } from '../BaseVideoPlayer.ts';

interface Video {
    id: string;
    title: string;
    name: string | null;
    videoTitle: string;
    subTitle: string;
    description: string;
    tags: string[];
    playCount: number;
    danmakuCount: number;
    commentCount: number;
    cover: string;
    m3u8: string;
}

/**
 * 首页视频模块
 */
export class HomeVideo extends BaseVideoPlayer {
    private readonly _playerVideoContainerSelector: string = '.js-home-video-container';

    private readonly _container: HTMLDivElement | null = document.querySelector(
        this._playerVideoContainerSelector
    ) as HTMLDivElement | null;

    constructor() {
        super();
        if (!this._container) {
            return;
        }
        this.init();
    }

    private init(): void {
        const configStr = this._container?.getAttribute('data-video-config');
        if (!configStr) {
            return;
        }

        try {
            const video = JSON.parse(configStr) as Video;
            this.play(video);
        } catch (err) {
            console.warn('Failed to parse video config:', err);
        }
    }

    private play(video: Video): void {
        if (!this._container) {
            return;
        }

        const videoId = video.id;

        const art = new Artplayer({
            container: this._container,
            url: video.m3u8,
            poster: video.cover,
            fullscreenWeb: true,
            fullscreen: true,
            controls: [],
            // 使用基类的通用配置
            ...this.getCommonArtplayerOptions(),
            plugins: [this.createDanmakuPlugin(videoId)],
        });

        // 全屏状态样式切换
        art.on('fullscreenWeb', (state) => {
            if (state) {
                this._container?.classList.add('is-web-fullscreen');
            } else {
                this._container?.classList.remove('is-web-fullscreen');
            }
        });

        art.on('fullscreen', (state) => {
            if (state) {
                this._container?.classList.add('is-fullscreen');
            } else {
                this._container?.classList.remove('is-fullscreen');
            }
        });

        // 使用基类的首次播放处理
        art.on('video:play', this.createFirstPlayHandler(videoId));
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个基于 Artplayer 视频播放器的首页视频模块实现,继承自 BaseVideoPlayer 基类。

主要组成部分

1. 类型定义 - Video 接口

interface Video {
    id: string;           // 视频唯一标识
    title: string;        // 标题
    name: string | null;  // 名称
    videoTitle: string;   // 视频标题
    subTitle: string;     // 副标题
    description: string;  // 描述
    tags: string[];       // 标签数组
    playCount: number;    // 播放次数
    danmakuCount: number; // 弹幕数量
    commentCount: number; // 评论数量
    cover: string;        // 封面图URL
    m3u8: string;        // 视频流地址(HLS格式)
}

2. HomeVideo 类

私有属性

  • _playerVideoContainerSelector: CSS选择器,用于定位视频容器
  • _container: 视频播放器的DOM容器元素

构造函数

constructor() {
    super();  // 调用基类构造函数
    if (!this._container) {
        return;  // 如果找不到容器,直接返回
    }
    this.init();  // 初始化视频播放器
}

核心方法

init() - 初始化方法

  1. 从容器的 data-video-config 属性获取视频配置
  2. 解析 JSON 配置数据
  3. 调用 play() 方法播放视频
  4. 包含错误处理机制

play(video: Video) - 播放方法

  1. 创建 Artplayer 实例,配置包括:

    • 容器元素
    • 视频URL (m3u8流)
    • 封面图
    • 全屏相关配置
    • 继承基类的通用配置 (getCommonArtplayerOptions())
    • 弹幕插件 (createDanmakuPlugin())
  2. 绑定全屏事件监听器

    • fullscreenWeb: 网页全屏状态变化时,添加/移除 is-web-fullscreen
    • fullscreen: 浏览器全屏状态变化时,添加/移除 is-fullscreen
  3. 绑定首次播放事件

    • 使用基类提供的 createFirstPlayHandler() 处理视频首次播放逻辑

设计特点

  1. 继承架构: 继承 BaseVideoPlayer,复用通用播放器配置和功能
  2. 数据驱动: 通过 HTML 的 data-* 属性传递视频配置
  3. 错误处理: 包含 JSON 解析的异常捕获
  4. 样式联动: 根据全屏状态动态添加CSS类,便于样式控制
  5. 模块化: 弹幕、首次播放等功能通过基类方法注入

使用场景

这个类用于在首页展示视频播放功能,支持 HLS 流媒体播放、弹幕、全屏等特性。

评论加载中...