npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@itshixun/qckeditor-plugin-video

v1.0.0

Published

CKEditor 5 plugin for video upload and playback

Readme

@itshixun/qckeditor-plugin-video

CKEditor 5 插件:视频上传与播放。在编辑器中插入 <video> 元素,支持本地上传和直接插入视频 URL,上传过程中显示进度条。

安装

npm install @itshixun/qckeditor-plugin-video
# 或
pnpm add @itshixun/qckeditor-plugin-video

依赖 ckeditor5(peerDependencies,需自行安装)。

基本使用

1. 引入样式

import '@itshixun/qckeditor-plugin-video/dist/index.css';

2. 注册插件

import { ClassicEditor } from 'ckeditor5';
import { QstVideo } from '@itshixun/qckeditor-plugin-video';

const editor = await ClassicEditor.create(element, {
  plugins: [
    // ... 其他插件
    QstVideo,
  ],
  toolbar: [
    // ... 其他按钮
    'uploadVideo',  // 工具栏按钮名称:上传视频
  ],
  qstVideo: {
    upload: {
      types: ['mp4', 'webm', 'ogg'],
      maxSize: 100 * 1024 * 1024, // 100MB
    },
    progress: {
      showProgressBar: true,
      useReadOnlyLock: false,
    },
  },
});

3. Vue 中使用

<script setup lang="ts">
import { ClassicEditor } from 'ckeditor5';
import { QstVideo } from '@itshixun/qckeditor-plugin-video';

const config = {
  editor: ClassicEditor,
  plugins: [/* ... */, QstVideo],
  toolbar: ['bold', 'italic', 'uploadVideo'],
  qstVideo: {
    upload: {
      types: ['mp4', 'webm'],
      maxSize: 50 * 1024 * 1024,
    },
  },
};
</script>

<template>
  <ckeditor :editor="config.editor" :config="config" />
</template>

配置项

| 属性 | 类型 | 必填 | 说明 | |------|------|------|------| | upload.types | string[] | 否 | 允许上传的文件扩展名列表,如 ['mp4', 'webm', 'ogg'] | | upload.maxSize | number | 否 | 最大文件大小(字节),超出会提示警告 | | progress.showProgressBar | boolean | 否 | 是否显示上传进度条,默认 true | | progress.useReadOnlyLock | boolean | 否 | 上传期间是否锁定编辑器为只读模式,默认 false |

上传配置

本插件依赖 CKEditor 5 的 FileRepository 插件处理文件上传。你需要配置一个自定义的 UploadAdapter,否则上传功能无法正常工作。

自定义 UploadAdapter 实现示例

import type { FileLoader, UploadAdapter } from 'ckeditor5';

class MyUploadAdapter implements UploadAdapter {
  private loader: FileLoader;
  private xhr?: XMLHttpRequest;

  constructor(loader: FileLoader) {
    this.loader = loader;
  }

  upload(): Promise<{ default: string }> {
    return this.loader.file.then(
      (file) =>
        new Promise((resolve, reject) => {
          this._initRequest();
          this._initListeners(resolve, reject, file!);
          this._sendRequest(file!);
        })
    );
  }

  abort(): void {
    if (this.xhr) {
      this.xhr.abort();
    }
  }

  private _initRequest(): void {
    const xhr = (this.xhr = new XMLHttpRequest());
    xhr.open('POST', 'https://your-api.com/upload', true);
    xhr.responseType = 'json';
  }

  private _initListeners(
    resolve: (value: { default: string }) => void,
    reject: (reason?: string) => void,
    file: File
  ): void {
    const xhr = this.xhr!;
    const loader = this.loader;
    const genericErrorText = `无法上传文件: ${file.name}.`;

    xhr.addEventListener('error', () => reject(genericErrorText));
    xhr.addEventListener('abort', () => reject());
    xhr.addEventListener('load', () => {
      const response = xhr.response;

      if (!response || response.error) {
        return reject(response?.error?.message || genericErrorText);
      }

      // 响应必须包含 default 字段(视频 URL)
      resolve({ default: response.url });
    });

    if (xhr.upload) {
      xhr.upload.addEventListener('progress', (evt) => {
        if (evt.lengthComputable) {
          loader.uploadTotal = evt.total;
          loader.uploaded = evt.loaded;
        }
      });
    }
  }

  private _sendRequest(file: File): void {
    const data = new FormData();
    data.append('file', file);
    this.xhr!.send(data);
  }
}

在编辑器中注册 UploadAdapter

function MyUploadAdapterPlugin(editor: any) {
  editor.plugins.get('FileRepository').createUploadAdapter = (loader: FileLoader) => {
    return new MyUploadAdapter(loader);
  };
}

const editor = await ClassicEditor.create(element, {
  plugins: [
    // ... 其他插件
    MyUploadAdapterPlugin, // 必须先注册,再注册 QstVideo
    QstVideo,
  ],
  toolbar: ['uploadVideo'],
});

重要:

  • MyUploadAdapterPlugin 必须在 QstVideo 之前注册到 plugins 数组中
  • 上传响应必须返回 { default: '视频URL' } 格式,default 字段会被提取为视频的 src
  • 上传失败时会自动移除占位视频元素,并通过 Notification 插件显示警告

命令说明

插件注册了两个命令,分别对应不同的使用场景:

videoInsert — 插入已有视频

通过视频 URL 直接插入已有视频(不上传):

editor.execute('videoInsert', {
  src: 'https://example.com/videos/demo.mp4',
  controls: true, // 可选,默认 true
});

uploadVideo — 上传本地视频

通常由工具栏按钮自动触发,也可手动调用:

const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = fileInput.files![0];

editor.execute('uploadVideo', { file });

输出 HTML

保存态(Data Downcast)

<figure class="video ck-qst-video">
  <video controls src="https://example.com/videos/demo.mp4"></video>
</figure>

编辑态(Editing Downcast)

编辑态会渲染为 CKEditor 5 的 Widget,带有选中高亮和进度条:

<figure class="video ck-qst-video ck-widget" contenteditable="false">
  <video controls src="https://example.com/videos/demo.mp4"></video>
  <!-- 上传时显示进度条 -->
  <div class="ck-qst-video-progress-bar" style="width: 45%"></div>
</figure>

数据兼容性

插件在 upcast(粘贴/加载 HTML)时兼容旧版格式:

| 新版本 | 旧版本 | |--------|--------| | <figure class="ck-qst-video"> | <figure class="video"> |

旧版 HTML 会被自动转换为新版数据模型,保存时输出新版格式(同时保留 video 类以兼容旧样式):

<!-- 旧版输入 -->
<figure class="video">
  <video controls src="..."></video>
</figure>

<!-- 新版输出 -->
<figure class="video ck-qst-video">
  <video controls src="..."></video>
</figure>

类型支持

导入插件后,EditorConfig 类型自动扩展,TypeScript 可直接识别 qstVideo 配置:

const config: EditorConfig = {
  // 无类型错误,qstVideo 已声明
  qstVideo: {
    upload: { types: ['mp4'], maxSize: 1024 * 1024 },
  },
};

导出 API

import {
  QstVideo,              // 主插件(Editing + Upload)
  QstVideoEditing,       // 编辑插件(Schema + Conversion + videoInsert 命令)
  QstVideoInsertCommand, // 插入已有视频的命令
  QstVideoUpload,        // 上传功能聚合插件
  QstVideoUploadEditing, // 上传编辑插件(自动上传处理 + 进度管理)
  QstVideoUploadUI,      // 上传 UI 插件(工具栏按钮)
  QstVideoUploadProgress, // 上传进度条插件
  QstVideoUploadCommand, // 上传视频的命令
  insertVideo,           // 工具函数:插入视频元素
  isVideoAllowed,        // 工具函数:检查是否允许插入视频
  type QstVideoConfig,
  type VideoInsertCommandOptions,
  type UploadVideoCommandOptions,
} from '@itshixun/qckeditor-plugin-video';