Bläddra i källkod

master: Fixed 接口对接 插件引入

gitboyzcf 2 veckor sedan
förälder
incheckning
479dfe53c9

+ 1 - 0
omnimatrix-video-player/.npmrc

@@ -0,0 +1 @@
+registry=https://npm.r-2.top

+ 39 - 0
omnimatrix-video-player/README.md

@@ -0,0 +1,39 @@
+# omnimatrix-video-player
+
+观曜视频播放包
+
+## 安装
+
+```bash
+pnpm i omnimatrix-video-player@latest
+```
+
+## 使用
+
+```html
+ <canvas id="video-demo" width="100%"></canvas>
+```
+
+```js
+
+import useWorker from 'omnimatrix-video-player'
+
+
+/** 
+ * function useWorker(url: string, className: string, device: string | null, callback?: Function): any
+    @param url — wss连接地址 wss://origin/VideoShow/Common?UUID=uuid&DeviceID=deviceID&Token=token
+    @param className — 选择器 .video-class | #video-class ...
+    @param device — 设备ID
+    @param callback — 画面渲染完成时的回调
+    @returns — 包含两个Web Worker和一个关闭函数的对象
+*/
+
+const workerObj = null
+
+// 测试地址 请更换自己的
+const wss = `wss://origin/VideoShow/Common?UUID=uuid&DeviceID=deviceID&Token=token`
+
+workerObj = useWorker(wss, "#video-demo", null, () => {
+  console.log("播放完成");
+});
+```

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
omnimatrix-video-player/lib/BqCyEDfM.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
omnimatrix-video-player/lib/C0jiH7OH.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
omnimatrix-video-player/lib/Ca3LKSSa.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
omnimatrix-video-player/lib/CfNebfuO.js


+ 1 - 0
omnimatrix-video-player/lib/D43TsmUs.js

@@ -0,0 +1 @@
+var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(e,n,o){return e(o={path:n,exports:{},require:function(e,n){return function(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}(null==n&&o.path)}},o.exports),o.exports}export{e as a,n as c};

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
omnimatrix-video-player/lib/DNrAGae2.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
omnimatrix-video-player/lib/QCSCNFjR.js


+ 42 - 0
omnimatrix-video-player/lib/core/package/package.json

@@ -0,0 +1,42 @@
+{
+  "name": "@ffmpeg/core",
+  "version": "0.12.5",
+  "description": "FFmpeg WebAssembly version (single thread)",
+  "main": "./dist/umd/ffmpeg-core.js",
+  "exports": {
+    ".": {
+      "import": "./dist/esm/ffmpeg-core.js",
+      "require": "./dist/umd/ffmpeg-core.js"
+    },
+    "./wasm": {
+      "import": "./dist/esm/ffmpeg-core.wasm",
+      "require": "./dist/umd/ffmpeg-core.wasm"
+    }
+  },
+  "files": [
+    "dist"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/ffmpegwasm/ffmpeg.wasm.git"
+  },
+  "keywords": [
+    "ffmpeg",
+    "WebAssembly",
+    "video",
+    "audio",
+    "transcode"
+  ],
+  "author": "Jerome Wu <jeromewus@gmail.com>",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/ffmpegwasm/ffmpeg.wasm/issues"
+  },
+  "engines": {
+    "node": ">=16.x"
+  },
+  "homepage": "https://github.com/ffmpegwasm/ffmpeg.wasm#readme",
+  "publishConfig": {
+    "access": "public"
+  }
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2337 - 0
omnimatrix-video-player/lib/core/package/pkg/esm/ffmpeg-core.js


BIN
omnimatrix-video-player/lib/core/package/pkg/esm/ffmpeg-core.wasm


+ 277 - 0
omnimatrix-video-player/lib/ffmpeg/classes.js

@@ -0,0 +1,277 @@
+import { FFMessageType } from "./const.js";
+import { getMessageID } from "./utils.js";
+import { ERROR_TERMINATED, ERROR_NOT_LOADED } from "./errors.js";
+/**
+ * Provides APIs to interact with ffmpeg web worker.
+ *
+ * @example
+ * ```ts
+ * const ffmpeg = new FFmpeg();
+ * ```
+ */
+export class FFmpeg {
+    #worker = null;
+    /**
+     * #resolves and #rejects tracks Promise resolves and rejects to
+     * be called when we receive message from web worker.
+     */
+    #resolves = {};
+    #rejects = {};
+    #logEventCallbacks = [];
+    #progressEventCallbacks = [];
+    loaded = false;
+    /**
+     * register worker message event handlers.
+     */
+    #registerHandlers = () => {
+        if (this.#worker) {
+            this.#worker.onmessage = ({ data: { id, type, data }, }) => {
+                switch (type) {
+                    case FFMessageType.LOAD:
+                        this.loaded = true;
+                        this.#resolves[id](data);
+                        break;
+                    case FFMessageType.MOUNT:
+                    case FFMessageType.UNMOUNT:
+                    case FFMessageType.EXEC:
+                    case FFMessageType.WRITE_FILE:
+                    case FFMessageType.READ_FILE:
+                    case FFMessageType.DELETE_FILE:
+                    case FFMessageType.RENAME:
+                    case FFMessageType.CREATE_DIR:
+                    case FFMessageType.LIST_DIR:
+                    case FFMessageType.DELETE_DIR:
+                        this.#resolves[id](data);
+                        break;
+                    case FFMessageType.LOG:
+                        this.#logEventCallbacks.forEach((f) => f(data));
+                        break;
+                    case FFMessageType.PROGRESS:
+                        this.#progressEventCallbacks.forEach((f) => f(data));
+                        break;
+                    case FFMessageType.ERROR:
+                        this.#rejects[id](data);
+                        break;
+                }
+                delete this.#resolves[id];
+                delete this.#rejects[id];
+            };
+        }
+    };
+    /**
+     * Generic function to send messages to web worker.
+     */
+    #send = ({ type, data }, trans = [], signal) => {
+        if (!this.#worker) {
+            return Promise.reject(ERROR_NOT_LOADED);
+        }
+        return new Promise((resolve, reject) => {
+            const id = getMessageID();
+            this.#worker && this.#worker.postMessage({ id, type, data }, trans);
+            this.#resolves[id] = resolve;
+            this.#rejects[id] = reject;
+            signal?.addEventListener("abort", () => {
+                reject(new DOMException(`Message # ${id} was aborted`, "AbortError"));
+            }, { once: true });
+        });
+    };
+    on(event, callback) {
+        if (event === "log") {
+            this.#logEventCallbacks.push(callback);
+        }
+        else if (event === "progress") {
+            this.#progressEventCallbacks.push(callback);
+        }
+    }
+    off(event, callback) {
+        if (event === "log") {
+            this.#logEventCallbacks = this.#logEventCallbacks.filter((f) => f !== callback);
+        }
+        else if (event === "progress") {
+            this.#progressEventCallbacks = this.#progressEventCallbacks.filter((f) => f !== callback);
+        }
+    }
+    /**
+     * Loads ffmpeg-core inside web worker. It is required to call this method first
+     * as it initializes WebAssembly and other essential variables.
+     *
+     * @category FFmpeg
+     * @returns `true` if ffmpeg core is loaded for the first time.
+     */
+    load = ({ classWorkerURL, ...config } = {}, { signal } = {}) => {
+        if (!this.#worker) {
+            this.#worker = classWorkerURL ?
+                new Worker(new URL(classWorkerURL, import.meta.url), {
+                    type: "module",
+                }) :
+                // We need to duplicated the code here to enable webpack
+                // to bundle worekr.js here.
+                new Worker(new URL("./worker.js", import.meta.url), {
+                    type: "module",
+                });
+            this.#registerHandlers();
+        }
+        return this.#send({
+            type: FFMessageType.LOAD,
+            data: config,
+        }, undefined, signal);
+    };
+    /**
+     * Execute ffmpeg command.
+     *
+     * @remarks
+     * To avoid common I/O issues, ["-nostdin", "-y"] are prepended to the args
+     * by default.
+     *
+     * @example
+     * ```ts
+     * const ffmpeg = new FFmpeg();
+     * await ffmpeg.load();
+     * await ffmpeg.writeFile("video.avi", ...);
+     * // ffmpeg -i video.avi video.mp4
+     * await ffmpeg.exec(["-i", "video.avi", "video.mp4"]);
+     * const data = ffmpeg.readFile("video.mp4");
+     * ```
+     *
+     * @returns `0` if no error, `!= 0` if timeout (1) or error.
+     * @category FFmpeg
+     */
+    exec = (
+    /** ffmpeg command line args */
+    args, 
+    /**
+     * milliseconds to wait before stopping the command execution.
+     *
+     * @defaultValue -1
+     */
+    timeout = -1, { signal } = {}) => this.#send({
+        type: FFMessageType.EXEC,
+        data: { args, timeout },
+    }, undefined, signal);
+    /**
+     * Terminate all ongoing API calls and terminate web worker.
+     * `FFmpeg.load()` must be called again before calling any other APIs.
+     *
+     * @category FFmpeg
+     */
+    terminate = () => {
+        const ids = Object.keys(this.#rejects);
+        // rejects all incomplete Promises.
+        for (const id of ids) {
+            this.#rejects[id](ERROR_TERMINATED);
+            delete this.#rejects[id];
+            delete this.#resolves[id];
+        }
+        if (this.#worker) {
+            this.#worker.terminate();
+            this.#worker = null;
+            this.loaded = false;
+        }
+    };
+    /**
+     * Write data to ffmpeg.wasm.
+     *
+     * @example
+     * ```ts
+     * const ffmpeg = new FFmpeg();
+     * await ffmpeg.load();
+     * await ffmpeg.writeFile("video.avi", await fetchFile("../video.avi"));
+     * await ffmpeg.writeFile("text.txt", "hello world");
+     * ```
+     *
+     * @category File System
+     */
+    writeFile = (path, data, { signal } = {}) => {
+        const trans = [];
+        if (data instanceof Uint8Array) {
+            trans.push(data.buffer);
+        }
+        return this.#send({
+            type: FFMessageType.WRITE_FILE,
+            data: { path, data },
+        }, trans, signal);
+    };
+    mount = (fsType, options, mountPoint) => {
+        const trans = [];
+        return this.#send({
+            type: FFMessageType.MOUNT,
+            data: { fsType, options, mountPoint },
+        }, trans);
+    };
+    unmount = (mountPoint) => {
+        const trans = [];
+        return this.#send({
+            type: FFMessageType.UNMOUNT,
+            data: { mountPoint },
+        }, trans);
+    };
+    /**
+     * Read data from ffmpeg.wasm.
+     *
+     * @example
+     * ```ts
+     * const ffmpeg = new FFmpeg();
+     * await ffmpeg.load();
+     * const data = await ffmpeg.readFile("video.mp4");
+     * ```
+     *
+     * @category File System
+     */
+    readFile = (path, 
+    /**
+     * File content encoding, supports two encodings:
+     * - utf8: read file as text file, return data in string type.
+     * - binary: read file as binary file, return data in Uint8Array type.
+     *
+     * @defaultValue binary
+     */
+    encoding = "binary", { signal } = {}) => this.#send({
+        type: FFMessageType.READ_FILE,
+        data: { path, encoding },
+    }, undefined, signal);
+    /**
+     * Delete a file.
+     *
+     * @category File System
+     */
+    deleteFile = (path, { signal } = {}) => this.#send({
+        type: FFMessageType.DELETE_FILE,
+        data: { path },
+    }, undefined, signal);
+    /**
+     * Rename a file or directory.
+     *
+     * @category File System
+     */
+    rename = (oldPath, newPath, { signal } = {}) => this.#send({
+        type: FFMessageType.RENAME,
+        data: { oldPath, newPath },
+    }, undefined, signal);
+    /**
+     * Create a directory.
+     *
+     * @category File System
+     */
+    createDir = (path, { signal } = {}) => this.#send({
+        type: FFMessageType.CREATE_DIR,
+        data: { path },
+    }, undefined, signal);
+    /**
+     * List directory contents.
+     *
+     * @category File System
+     */
+    listDir = (path, { signal } = {}) => this.#send({
+        type: FFMessageType.LIST_DIR,
+        data: { path },
+    }, undefined, signal);
+    /**
+     * Delete an empty directory.
+     *
+     * @category File System
+     */
+    deleteDir = (path, { signal } = {}) => this.#send({
+        type: FFMessageType.DELETE_DIR,
+        data: { path },
+    }, undefined, signal);
+}

+ 22 - 0
omnimatrix-video-player/lib/ffmpeg/const.js

@@ -0,0 +1,22 @@
+export const MIME_TYPE_JAVASCRIPT = "text/javascript";
+export const MIME_TYPE_WASM = "application/wasm";
+export const CORE_VERSION = "0.12.6";
+export const CORE_URL = `https://unpkg.com/@ffmpeg/core@${CORE_VERSION}/dist/umd/ffmpeg-core.js`;
+export var FFMessageType;
+(function (FFMessageType) {
+    FFMessageType["LOAD"] = "LOAD";
+    FFMessageType["EXEC"] = "EXEC";
+    FFMessageType["WRITE_FILE"] = "WRITE_FILE";
+    FFMessageType["READ_FILE"] = "READ_FILE";
+    FFMessageType["DELETE_FILE"] = "DELETE_FILE";
+    FFMessageType["RENAME"] = "RENAME";
+    FFMessageType["CREATE_DIR"] = "CREATE_DIR";
+    FFMessageType["LIST_DIR"] = "LIST_DIR";
+    FFMessageType["DELETE_DIR"] = "DELETE_DIR";
+    FFMessageType["ERROR"] = "ERROR";
+    FFMessageType["DOWNLOAD"] = "DOWNLOAD";
+    FFMessageType["PROGRESS"] = "PROGRESS";
+    FFMessageType["LOG"] = "LOG";
+    FFMessageType["MOUNT"] = "MOUNT";
+    FFMessageType["UNMOUNT"] = "UNMOUNT";
+})(FFMessageType || (FFMessageType = {}));

+ 6 - 0
omnimatrix-video-player/lib/ffmpeg/empty.mjs

@@ -0,0 +1,6 @@
+// File to be imported in node enviroments
+export class FFmpeg {
+    constructor() {
+        throw new Error("ffmpeg.wasm does not support nodejs");
+    }
+}

+ 4 - 0
omnimatrix-video-player/lib/ffmpeg/errors.js

@@ -0,0 +1,4 @@
+export const ERROR_UNKNOWN_MESSAGE_TYPE = new Error("unknown message type");
+export const ERROR_NOT_LOADED = new Error("ffmpeg is not loaded, call `await ffmpeg.load()` first");
+export const ERROR_TERMINATED = new Error("called FFmpeg.terminate()");
+export const ERROR_IMPORT_FAILURE = new Error("failed to import ffmpeg-core.js");

+ 1 - 0
omnimatrix-video-player/lib/ffmpeg/index.js

@@ -0,0 +1 @@
+export * from "./classes.js";

+ 9 - 0
omnimatrix-video-player/lib/ffmpeg/types.js

@@ -0,0 +1,9 @@
+export var FFFSType;
+(function (FFFSType) {
+    FFFSType["MEMFS"] = "MEMFS";
+    FFFSType["NODEFS"] = "NODEFS";
+    FFFSType["NODERAWFS"] = "NODERAWFS";
+    FFFSType["IDBFS"] = "IDBFS";
+    FFFSType["WORKERFS"] = "WORKERFS";
+    FFFSType["PROXYFS"] = "PROXYFS";
+})(FFFSType || (FFFSType = {}));

+ 7 - 0
omnimatrix-video-player/lib/ffmpeg/utils.js

@@ -0,0 +1,7 @@
+/**
+ * Generate an unique message ID.
+ */
+export const getMessageID = (() => {
+    let messageID = 0;
+    return () => messageID++;
+})();

+ 151 - 0
omnimatrix-video-player/lib/ffmpeg/worker.js

@@ -0,0 +1,151 @@
+/// <reference no-default-lib="true" />
+/// <reference lib="esnext" />
+/// <reference lib="webworker" />
+import { CORE_URL, FFMessageType } from "./const.js";
+import { ERROR_UNKNOWN_MESSAGE_TYPE, ERROR_NOT_LOADED, ERROR_IMPORT_FAILURE, } from "./errors.js";
+let ffmpeg;
+const load = async ({ coreURL: _coreURL, wasmURL: _wasmURL, workerURL: _workerURL, }) => {
+    const first = !ffmpeg;
+    try {
+        if (!_coreURL)
+            _coreURL = CORE_URL;
+        // when web worker type is `classic`.
+        importScripts(_coreURL);
+    }
+    catch {
+        if (!_coreURL)
+            _coreURL = CORE_URL.replace('/umd/', '/esm/');
+        // when web worker type is `module`.
+        self.createFFmpegCore = (await import(
+        /* webpackIgnore: true */ /* @vite-ignore */ _coreURL)).default;
+        if (!self.createFFmpegCore) {
+            throw ERROR_IMPORT_FAILURE;
+        }
+    }
+    const coreURL = _coreURL;
+    const wasmURL = _wasmURL ? _wasmURL : _coreURL.replace(/.js$/g, ".wasm");
+    const workerURL = _workerURL
+        ? _workerURL
+        : _coreURL.replace(/.js$/g, ".worker.js");
+    ffmpeg = await self.createFFmpegCore({
+        // Fix `Overload resolution failed.` when using multi-threaded ffmpeg-core.
+        // Encoded wasmURL and workerURL in the URL as a hack to fix locateFile issue.
+        mainScriptUrlOrBlob: `${coreURL}#${btoa(JSON.stringify({ wasmURL, workerURL }))}`,
+    });
+    ffmpeg.setLogger((data) => self.postMessage({ type: FFMessageType.LOG, data }));
+    ffmpeg.setProgress((data) => self.postMessage({
+        type: FFMessageType.PROGRESS,
+        data,
+    }));
+    return first;
+};
+const exec = ({ args, timeout = -1 }) => {
+    ffmpeg.setTimeout(timeout);
+    ffmpeg.exec(...args);
+    const ret = ffmpeg.ret;
+    ffmpeg.reset();
+    return ret;
+};
+const writeFile = ({ path, data }) => {
+    ffmpeg.FS.writeFile(path, data);
+    return true;
+};
+const readFile = ({ path, encoding }) => ffmpeg.FS.readFile(path, { encoding });
+// TODO: check if deletion works.
+const deleteFile = ({ path }) => {
+    ffmpeg.FS.unlink(path);
+    return true;
+};
+const rename = ({ oldPath, newPath }) => {
+    ffmpeg.FS.rename(oldPath, newPath);
+    return true;
+};
+// TODO: check if creation works.
+const createDir = ({ path }) => {
+    ffmpeg.FS.mkdir(path);
+    return true;
+};
+const listDir = ({ path }) => {
+    const names = ffmpeg.FS.readdir(path);
+    const nodes = [];
+    for (const name of names) {
+        const stat = ffmpeg.FS.stat(`${path}/${name}`);
+        const isDir = ffmpeg.FS.isDir(stat.mode);
+        nodes.push({ name, isDir });
+    }
+    return nodes;
+};
+// TODO: check if deletion works.
+const deleteDir = ({ path }) => {
+    ffmpeg.FS.rmdir(path);
+    return true;
+};
+const mount = ({ fsType, options, mountPoint }) => {
+    const str = fsType;
+    const fs = ffmpeg.FS.filesystems[str];
+    if (!fs)
+        return false;
+    ffmpeg.FS.mount(fs, options, mountPoint);
+    return true;
+};
+const unmount = ({ mountPoint }) => {
+    ffmpeg.FS.unmount(mountPoint);
+    return true;
+};
+self.onmessage = async ({ data: { id, type, data: _data }, }) => {
+    const trans = [];
+    let data;
+    try {
+        if (type !== FFMessageType.LOAD && !ffmpeg)
+            throw ERROR_NOT_LOADED; // eslint-disable-line
+        switch (type) {
+            case FFMessageType.LOAD:
+                data = await load(_data);
+                break;
+            case FFMessageType.EXEC:
+                data = exec(_data);
+                break;
+            case FFMessageType.WRITE_FILE:
+                data = writeFile(_data);
+                break;
+            case FFMessageType.READ_FILE:
+                data = readFile(_data);
+                break;
+            case FFMessageType.DELETE_FILE:
+                data = deleteFile(_data);
+                break;
+            case FFMessageType.RENAME:
+                data = rename(_data);
+                break;
+            case FFMessageType.CREATE_DIR:
+                data = createDir(_data);
+                break;
+            case FFMessageType.LIST_DIR:
+                data = listDir(_data);
+                break;
+            case FFMessageType.DELETE_DIR:
+                data = deleteDir(_data);
+                break;
+            case FFMessageType.MOUNT:
+                data = mount(_data);
+                break;
+            case FFMessageType.UNMOUNT:
+                data = unmount(_data);
+                break;
+            default:
+                throw ERROR_UNKNOWN_MESSAGE_TYPE;
+        }
+    }
+    catch (e) {
+        self.postMessage({
+            id,
+            type: FFMessageType.ERROR,
+            data: e.toString(),
+        });
+        return;
+    }
+    if (data instanceof Uint8Array) {
+        trans.push(data.buffer);
+    }
+    self.postMessage({ id, type, data }, trans);
+};

+ 1 - 0
omnimatrix-video-player/omnimatrix-video-player.js

@@ -0,0 +1 @@
+export{u as default}from"./lib/CfNebfuO.js";import"./lib/D43TsmUs.js";

+ 21 - 0
omnimatrix-video-player/package.json

@@ -0,0 +1,21 @@
+{
+  "name": "omnimatrix-video-player",
+  "version": "1.0.1",
+  "type": "module",
+  "main": "./omnimatrix-video-player.js",
+  "module": "./omnimatrix-video-player.js",
+  "license": "ISC",
+  "keywords": "video, player",
+  "author": "boyzcf <boyzcf@qq.com>",
+  "repository": "http://39.100.237.43:3000/wangchao/WebSdk_Demo.git",
+  "exports": {
+    ".": "./omnimatrix-video-player.js"
+  },
+  "description": "观曜视频播放包",
+  "engines": {
+    "node": ">=18.17.0"
+  },
+  "publishConfig": {
+    "registry": "https://npm.r-2.top/"
+  }
+}

+ 2 - 0
package.json

@@ -20,7 +20,9 @@
     "@unhead/vue": "catalog:frontend",
     "@unocss/reset": "catalog:frontend",
     "@vueuse/core": "catalog:frontend",
+    "axios": "catalog:frontend",
     "nprogress": "catalog:frontend",
+    "omnimatrix-video-player": "file:omnimatrix-video-player",
     "pinia": "catalog:frontend",
     "vee-validate": "catalog:frontend",
     "viewerjs": "catalog:frontend",

+ 51 - 9
pnpm-lock.yaml

@@ -128,6 +128,9 @@ catalogs:
     '@vueuse/core':
       specifier: ^13.0.0
       version: 13.0.0
+    axios:
+      specifier: ^1.8.4
+      version: 1.8.4
     nprogress:
       specifier: ^0.2.0
       version: 0.2.0
@@ -150,8 +153,8 @@ catalogs:
       specifier: ^4.5.0
       version: 4.5.0
     vuetify:
-      specifier: ^3.7.19
-      version: 3.7.19
+      specifier: ^3.8.0
+      version: 3.8.1
   types:
     '@types/markdown-it-link-attributes':
       specifier: ^3.0.5
@@ -178,9 +181,15 @@ importers:
       '@vueuse/core':
         specifier: catalog:frontend
         version: 13.0.0(vue@3.5.13(typescript@5.8.2))
+      axios:
+        specifier: catalog:frontend
+        version: 1.8.4
       nprogress:
         specifier: catalog:frontend
         version: 0.2.0
+      omnimatrix-video-player:
+        specifier: file:omnimatrix-video-player
+        version: file:omnimatrix-video-player
       pinia:
         specifier: catalog:frontend
         version: 3.0.1(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))
@@ -201,7 +210,7 @@ importers:
         version: 4.5.0(vue@3.5.13(typescript@5.8.2))
       vuetify:
         specifier: catalog:frontend
-        version: 3.7.19(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))
+        version: 3.8.1(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))
     devDependencies:
       '@antfu/eslint-config':
         specifier: catalog:dev
@@ -2635,6 +2644,9 @@ packages:
   aws4@1.12.0:
     resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
 
+  axios@1.8.4:
+    resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==}
+
   babel-plugin-polyfill-corejs2@0.3.3:
     resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==}
     peerDependencies:
@@ -3582,6 +3594,15 @@ packages:
   flatted@3.3.1:
     resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
 
+  follow-redirects@1.15.9:
+    resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+
   for-each@0.3.3:
     resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
 
@@ -4637,6 +4658,10 @@ packages:
   ohash@2.0.11:
     resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
 
+  omnimatrix-video-player@file:omnimatrix-video-player:
+    resolution: {directory: omnimatrix-video-player, type: directory}
+    engines: {node: '>=18.17.0'}
+
   on-finished@2.4.1:
     resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
     engines: {node: '>= 0.8'}
@@ -4880,6 +4905,9 @@ packages:
   proxy-from-env@1.0.0:
     resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==}
 
+  proxy-from-env@1.1.0:
+    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
   pump@3.0.0:
     resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
 
@@ -5963,14 +5991,14 @@ packages:
       typescript:
         optional: true
 
-  vuetify@3.7.19:
-    resolution: {integrity: sha512-RrUpdBOGWGcT2oNLqXBiqYRudKTFqxTfr8lISl51Cuo80cfgRmbyDnlQQRWxE4QSNxcqXk6ZzBoJRUEXpW9C/w==}
+  vuetify@3.8.1:
+    resolution: {integrity: sha512-3qReKBBWIIdJJmwnFU1blVIKHDtnLfIP7kk0MwUrrfjYkWmsDpsymtDnsukkTCnlJ1WvhLr64eQFosr0RVbj9w==}
     engines: {node: ^12.20 || >=14.13}
     peerDependencies:
       typescript: '>=4.7'
-      vite-plugin-vuetify: '>=1.0.0'
-      vue: ^3.3.0
-      webpack-plugin-vuetify: '>=2.0.0'
+      vite-plugin-vuetify: '>=2.1.0'
+      vue: ^3.5.0
+      webpack-plugin-vuetify: '>=3.1.0'
     peerDependenciesMeta:
       typescript:
         optional: true
@@ -8723,6 +8751,14 @@ snapshots:
 
   aws4@1.12.0: {}
 
+  axios@1.8.4:
+    dependencies:
+      follow-redirects: 1.15.9
+      form-data: 4.0.2
+      proxy-from-env: 1.1.0
+    transitivePeerDependencies:
+      - debug
+
   babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.25.2):
     dependencies:
       '@babel/compat-data': 7.25.2
@@ -9934,6 +9970,8 @@ snapshots:
 
   flatted@3.3.1: {}
 
+  follow-redirects@1.15.9: {}
+
   for-each@0.3.3:
     dependencies:
       is-callable: 1.2.7
@@ -11147,6 +11185,8 @@ snapshots:
 
   ohash@2.0.11: {}
 
+  omnimatrix-video-player@file:omnimatrix-video-player: {}
+
   on-finished@2.4.1:
     dependencies:
       ee-first: 1.1.1
@@ -11376,6 +11416,8 @@ snapshots:
 
   proxy-from-env@1.0.0: {}
 
+  proxy-from-env@1.1.0: {}
+
   pump@3.0.0:
     dependencies:
       end-of-stream: 1.4.4
@@ -12689,7 +12731,7 @@ snapshots:
     optionalDependencies:
       typescript: 5.8.2
 
-  vuetify@3.7.19(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)):
+  vuetify@3.8.1(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)):
     dependencies:
       vue: 3.5.13(typescript@5.8.2)
     optionalDependencies:

+ 2 - 1
pnpm-workspace.yaml

@@ -50,6 +50,7 @@ catalogs:
     '@unhead/vue': ^2.0.2
     '@unocss/reset': ^66.1.0-beta.7
     '@vueuse/core': ^13.0.0
+    axios: ^1.8.4
     nprogress: ^0.2.0
     pinia: ^3.0.1
     vee-validate: ^4.0.0
@@ -57,7 +58,7 @@ catalogs:
     vue: ^3.5.13
     vue-i18n: ^11.1.2
     vue-router: ^4.5.0
-    vuetify: ^3.7.19
+    vuetify: ^3.8.0
 
   types:
     '@types/markdown-it-link-attributes': ^3.0.5

+ 121 - 106
src/components/AddBall.vue

@@ -1,135 +1,150 @@
 <script setup lang="ts">
-import { useField, useForm } from 'vee-validate'
+import axios from 'axios'
 
 const dialog = defineModel<boolean>('dialog', { type: Boolean, default: false })
+const system = useSystemStore()
 
-const phone = useField<string>('')
-const email = useField<string>('')
-const select = useField<string>('')
-const select2 = useField<string>('1')
-const select3 = useField<string>('1')
-
-const { values, handleReset } = useForm({
-  validationSchema: {
-    // select(value:any) {
-    //   if (value)
-    //     return true
-
-    //   return 'Select an item.'
-    // },
-  },
-})
-
-const selectOptions = ref([{ label: '海康原汁原味', value: '1' }, { label: '海康模组但串口通信(布控球)', value: '2' }])
-const select2Options = ref([{ label: '+', value: '1' }, { label: '-', value: '2' }])
+const name = ref('')
+const ip = ref('')
+const typeV = ref('HikSdk')
+const port = ref(8000)
+const user = ref('')
+const password = ref('')
+const rtspUrl = ref('')
+const loading = ref(false)
+const typeVOptions = ref([{ label: '海康原汁原味', value: 'HikSdk' }, { label: '海康模组但串口通信(布控球)', value: 'BuKongQiu' }])
 
 function resetHide(isActive: Ref<boolean>) {
-  handleReset()
+  // handleReset()
   isActive.value = false
 }
-function submitWrapper(isActive: Ref<boolean>) {
-  console.log(values)
-  isActive.value = false
+
+const rules = {
+  name: [
+    (value: string) => {
+      if (value)
+        return true
+      return '请输入球机名称'
+    },
+  ],
+  ip: [
+    (value: string) => {
+      if (!value) {
+        return '请输入球机IP'
+      }
+      else if (/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/.test(value)) {
+        return true
+      }
+      else {
+        return '请输入正确的IP地址'
+      }
+    },
+  ],
+  port: [
+    (value: number) => {
+      if (value)
+        return true
+      return '请输入球机私有协议端口'
+    },
+  ],
+  typeV: [
+    (value: string) => {
+      if (value)
+        return true
+      return '请选择球机类型'
+    },
+  ],
+  user: [
+    (value: string) => {
+      if (value)
+        return true
+      return '请输入SDK用户名'
+    },
+  ],
+  password: [
+    (value: string) => {
+      if (value)
+        return true
+      return '请输入SDK密码'
+    },
+  ],
+  rtspUrl: [
+    (value: string) => {
+      if (value)
+        return true
+      return '请输入RTSP地址'
+    },
+  ],
+}
+
+async function submit(event: any) {
+  loading.value = true
+  const results = await event
+  if (results.valid) {
+    axios.put(`https://${system.globalConfig.serverUrl}/api/BallCamera/Register`, {
+      Type: typeV,
+      Name: name,
+      Ip: ip,
+      Port: port,
+      User: user,
+      Password: password,
+      RtspUrl: rtspUrl,
+      Channel: 1,
+    }, { headers: { Deviceid: system.currentDevice } }).then((res) => {
+      axios.post(`https://${system.globalConfig.serverUrl}/api/BallCamera/Matrix?CameraId=${res.data.data}`, {
+        P_Start: 0.0,
+        P_Max: 360,
+        p_Positive_Direction: '+',
+        T_Start: 0.0,
+        T_Max: 180,
+        T_Positive_Direction: '+',
+        Matrix: [],
+        InvMatrix: [],
+        PointSet: {},
+      }, { headers: { Deviceid: system.currentDevice } }).then(() => {
+        loading.value = false
+        dialog.value = false
+        system.getBallCameraList()
+      })
+    })
+  }
+  else {
+    loading.value = false
+  }
 }
 </script>
 
 <template>
-  <v-dialog
-    v-model="dialog"
-    max-width="600"
-  >
+  <v-dialog v-model="dialog" max-width="600">
     <template #activator="{ props: activatorProps }">
       <slot name="activator" :props="activatorProps" />
     </template>
 
     <template #default="{ isActive }">
-      <v-card
-        prepend-icon="mdi-plus-thick"
-        title="添加球机"
-      >
+      <v-card prepend-icon="mdi-plus" title="添加球机">
         <v-card-item>
-          <form>
+          <v-form validate-on="submit lazy" :disabled="!system.globalConfig.serverUrl" @submit.prevent="submit">
             <v-select
-              v-model="select.value.value"
-              :error-messages="select.errorMessage.value"
-              :items="selectOptions"
-              item-title="label"
-              item-value="value"
-              label="球机类型"
+              v-model="typeV" :items="typeVOptions"
+              :rules="rules.typeV" item-title="label" item-value="value" label="球机类型"
             />
-            <v-row>
-              <v-col>
-                <v-sheet>
-                  <v-text-field
-                    v-model="phone.value.value"
-                    :error-messages="phone.errorMessage.value"
-                    label="P 起始值"
-                  />
-                </v-sheet>
-              </v-col>
+            <v-text-field v-model="name" :rules="rules.name" label="球机名称" />
 
-              <v-col>
-                <v-sheet>
-                  <v-text-field
-                    v-model="phone.value.value"
-                    :error-messages="phone.errorMessage.value"
-                    label="T 起始值"
-                  />
-                </v-sheet>
-              </v-col>
-            </v-row>
-            <v-row>
-              <v-col>
-                <v-sheet>
-                  <v-select
-                    v-model="select2.value.value"
-                    :error-messages="select2.errorMessage.value"
-                    :items="select2Options"
-                    item-title="label"
-                    item-value="value"
-                    label="P 正方向"
-                  />
-                </v-sheet>
-              </v-col>
-              <v-col>
-                <v-sheet>
-                  <v-select
-                    v-model="select3.value.value"
-                    :error-messages="select3.errorMessage.value"
-                    :items="select2Options"
-                    item-title="label"
-                    item-value="value"
-                    label="T 正方向"
-                  />
-                </v-sheet>
-              </v-col>
-            </v-row>
-            <v-text-field
-              v-model="phone.value.value"
-              :error-messages="phone.errorMessage.value"
-              label="球机名称"
-            />
-
-            <v-text-field
-              v-model="email.value.value"
-              :error-messages="email.errorMessage.value"
-              label="球机 IP"
-            />
-            <v-text-field
-              v-model="email.value.value"
-              :error-messages="email.errorMessage.value"
-              label="球机私有协议端口"
+            <v-text-field v-model="ip" :rules="rules.ip" label="球机 IP" />
+            <v-number-input
+              v-model="port"
+              :rules="rules.port" :reverse="false"
+              control-variant="stacked" label="球机私有协议端口" :hide-input="false" inset
             />
+            <v-text-field v-model="user" :rules="rules.user" label="SDK 用户名" />
+            <v-text-field v-model="password" :rules="rules.password" label="SDK 密码" />
+            <v-text-field v-model="rtspUrl" :rules="rules.rtspUrl" label="RTSP 地址" />
 
             <v-divider />
 
             <v-card-actions>
               <v-spacer />
-              <v-btn
-                border
-                class="me-4"
-                @click="submitWrapper(isActive)"
-              >
+              <v-btn :loading="loading" border class="me-4" type="submit">
                 提交
               </v-btn>
 
@@ -137,7 +152,7 @@ function submitWrapper(isActive: Ref<boolean>) {
                 取消
               </v-btn>
             </v-card-actions>
-          </form>
+          </v-form>
         </v-card-item>
       </v-card>
     </template>

+ 60 - 12
src/components/BallCameraSettings.vue

@@ -2,14 +2,12 @@
 import Viewer from 'viewerjs'
 import 'viewerjs/dist/viewer.css'
 
-const items = ref([
-  'Item 1',
-  'Item 2',
-  'Item 3',
-  'Item 4',
-])
+const positiveDirectionPOptions = ref([{ label: '+', value: '+' }, { label: '-', value: '-' }])
+const positiveDirectionTOptions = ref([{ label: '+', value: '+' }, { label: '-', value: '-' }])
+
+const system = useSystemStore()
 
-const selectV = ref('')
+const currentBallCamera = ref('')
 
 const addDialogV = ref(false)
 
@@ -18,21 +16,25 @@ const tools = ref([
   {
     type: 'top',
     icon: '👆',
+    value: 3,
   },
   {},
   {
     type: 'left',
     icon: '👈',
+    value: 1,
   },
   {},
   {
     type: 'right',
     icon: '👉',
+    value: 2,
   },
   {},
   {
     type: 'bottom',
     icon: '👇',
+    value: 4,
   },
   {},
 ])
@@ -89,6 +91,8 @@ function colorReset(id: number) {
 
 // 删除标记
 function del(id: number) {
+  if (!id)
+    return
   mark.value = mark.value.filter(v => v.id !== id)
   markClone.value = markClone.value.filter(v => v.id !== id)
   currentMark.value = mark.value[mark.value.length - 1]
@@ -187,7 +191,15 @@ onMounted(() => {
   <v-row>
     <v-col class="relative" cols="12" md="6">
       <div class="flex-1">
-        <v-select v-model="selectV" :items="items" label="球机列表">
+        <v-select
+          v-if="system.type?.Baseline === 'BoGuan'"
+          v-model="system.currentDevice" :items="system.deviceList" item-title="name" item-value="id"
+          label="选择设备"
+          @update:model-value="system.getBallCameraList"
+        />
+        <v-select
+          v-model="currentBallCamera" :items="system.ballCameraList" label="球机列表" item-title="name" item-value="id" @update:model-value="system.getBallCameraInfo"
+        >
           <template #append>
             <add-ball v-model:dialog="addDialogV">
               <template #activator="{ props: activatorProps }">
@@ -221,6 +233,43 @@ onMounted(() => {
             </v-dialog>
           </template>
         </v-select>
+        <v-row>
+          <v-col>
+            <v-sheet>
+              <v-number-input
+                v-model="system.positiveConfig.P_Start" control-variant="stacked" :precision="1"
+                hide-details="auto" label="P 起始值"
+              />
+            </v-sheet>
+          </v-col>
+
+          <v-col>
+            <v-sheet>
+              <v-number-input
+                v-model="system.positiveConfig.T_Start" control-variant="stacked" :precision="1"
+                hide-details="auto" label="T 起始值"
+              />
+            </v-sheet>
+          </v-col>
+        </v-row>
+        <v-row>
+          <v-col>
+            <v-sheet>
+              <v-select
+                v-model="system.positiveConfig.p" :items="positiveDirectionPOptions" item-title="label"
+                item-value="value" label="P 正方向"
+              />
+            </v-sheet>
+          </v-col>
+          <v-col>
+            <v-sheet>
+              <v-select
+                v-model="system.positiveConfig.t" :items="positiveDirectionTOptions" item-title="label"
+                item-value="value" label="T 正方向"
+              />
+            </v-sheet>
+          </v-col>
+        </v-row>
 
         <!-- 视频播放 -->
         <div class="p-1">
@@ -275,14 +324,13 @@ onMounted(() => {
           <img id="uploadImage" class="w-full" :src="imgSrc" alt="图片预览" @load="imgSrcLoad">
           <v-empty-state v-if="!imgSrcLoading && !imgSrc" icon="mdi-image-broken-variant" title="暂无上传图片" />
           <v-icon
-            v-for="(item, index) in mark"
-            :key="index"
-            :style="{
+            v-for="(item, index) in mark" :key="index" :style="{
               top: `${item.y}px`,
               left: `${item.x}px`,
               textShadow: `0 -1px #fff, 1px 0px #fff, 0 1px #fff, -1px 0 #fff,
           -1px -1px #fff, 1px 1px #fff, 1px -1px #fff, -1px 1px #fff`,
-            }" :color="item.color" icon="mdi-plus-thick" class="absolute bottom-0" variant="tonal" @click="markClick(item)"
+            }" :color="item.color" icon="mdi-plus-thick" class="absolute bottom-0" variant="tonal"
+            @click="markClick(item)"
           />
         </div>
         <div class="flex flex-col items-center">

+ 6 - 1
src/pages/index.vue

@@ -52,9 +52,14 @@ useHead({
       />
       <v-text-field v-model="system.globalConfig.serverUrl" clearable label="服务地址" prepend-icon="$vuetify" />
     </div>
+    <div>
+      <v-btn block color="blue" @click="system.saveGlobalConfig(() => { drawer = false })">
+        应用
+      </v-btn>
+    </div>
   </v-navigation-drawer>
   <div>
-    <v-fab icon="i-carbon-menu" app @click="drawer = !drawer" />
+    <v-fab icon="mdi-cog" app @click="drawer = !drawer" />
     <v-tabs
       v-model="currentItem"
       align-tabs="center"

+ 132 - 1
src/stores/system.ts

@@ -1,5 +1,52 @@
+import axios from 'axios'
 import { acceptHMRUpdate, defineStore } from 'pinia'
 
+axios.defaults.timeout = 3000
+axios.interceptors.response.use((response) => {
+  return response
+}, (error) => {
+  console.error(error)
+  return Promise.reject(error)
+})
+
+interface IVersion {
+  Baseline: string
+  SubSystem: string
+  Version: string
+}
+
+interface IOptions {
+  id: string
+  name: string
+}
+interface IDeviceListOptions extends IOptions {
+  wssAddress: string
+}
+
+interface IBallCameraInfo {
+  Type: string
+  Name: string
+  Ip: string
+  Port: number | 8000
+  User: string
+  Password: string
+  RtspUrl: string
+  Matrix: {
+    P_Start: number
+    P_Max: number
+    p_Positive_Direction: '+' | '-'
+    T_Start: number
+    T_Max: number
+    T_Positive_Direction: '+' | '-'
+    Matrix: number[]
+    InvMatrix: number[]
+    PointSet: {
+      [key: string]: string
+    }
+  }
+  Channel: number
+}
+
 export const useSystemStore = defineStore('system', () => {
   interface GlobalConfig {
     imgUrl: string
@@ -7,16 +54,100 @@ export const useSystemStore = defineStore('system', () => {
   }
   const globalConfig = reactive<GlobalConfig>({
     imgUrl: '',
-    serverUrl: '192.168.10.140:8081',
+    serverUrl: '192.168.211.3',
   })
+  const ballCameraList = ref<IOptions[]>([])
+  const type = ref<IVersion>()
+  const deviceList = ref<IDeviceListOptions[]>([])
+  const ballCameraInfo = ref<IBallCameraInfo>()
+  const positiveConfig = reactive({
+    p: '+',
+    t: '+',
+    P_Start: 0.0,
+    T_Start: 0.0,
+  })
+  const currentDevice = ref()
 
   function setGlobalConfig(config: GlobalConfig) {
     Object.assign(globalConfig, config)
   }
 
+  async function saveGlobalConfig(callback: () => void) {
+    if (globalConfig.serverUrl) {
+      getVersion()
+      callback()
+    }
+  }
+
+  function getBallCameraList() {
+    if (globalConfig.serverUrl) {
+      axios.get(`https://${globalConfig.serverUrl}/api/BallCamera/List`,{ headers:{ 'Deviceid': currentDevice.value }}).then((res) => {
+        ballCameraList.value = []
+        Object.keys(res.data.data).forEach((key) => {
+          ballCameraList.value.push({
+            id: key,
+            name: res.data.data[key],
+          })
+        })
+      })
+    }
+  }
+
+  function getVersion() {
+    if (globalConfig.serverUrl) {
+      axios.get(`https://${globalConfig.serverUrl}/Version`).then((res) => {
+        type.value = res.data.data
+        if(type.value?.Baseline === 'BoGuan'){
+          getDeviceList()
+        }else{
+          getBallCameraList()
+        }
+      }).catch(() => {
+        window.open(`https://${globalConfig.serverUrl}`, '_blank')
+      })
+    }
+  }
+  async function getDeviceList(): Promise<any> {
+    return new Promise((resolve) => {
+      if (globalConfig.serverUrl) {
+        axios.get(`https://${globalConfig.serverUrl}/api/device`).then((res) => {
+          deviceList.value = res.data.data.map((item: any) => {
+            return {
+              id: item.Id,
+              name: item.Alias,
+              wssAddress: item.WssAddress,
+            }
+          })
+          resolve(true)
+        })
+      }
+    })
+  }
+
+  function getBallCameraInfo(CameraId: string) {
+    if (globalConfig.serverUrl) {
+      axios.get(`https://${globalConfig.serverUrl}/api/BallCamera/Info?CameraId=${CameraId}`).then((res) => {
+        ballCameraInfo.value = res.data.data
+        positiveConfig.p = res.data.data.Matrix.p_Positive_Direction
+        positiveConfig.t = res.data.data.Matrix.T_Positive_Direction
+        positiveConfig.P_Start = res.data.data.Matrix.P_Start
+        positiveConfig.T_Start = res.data.data.Matrix.T_Start
+      })
+    }
+  }
+
   return {
     globalConfig,
+    positiveConfig,
     setGlobalConfig,
+    saveGlobalConfig,
+    ballCameraList,
+    type,
+    deviceList,
+    ballCameraInfo,
+    getBallCameraInfo,
+    getBallCameraList,
+    currentDevice
   }
 })
 

+ 1 - 1
tsconfig.json

@@ -34,5 +34,5 @@
       "@vue-macros/volar/define-slots"
     ]
   },
-  "exclude": ["dist", "node_modules", "cypress"]
+  "exclude": ["dist", "node_modules", "cypress", "omnimatrix-video-player"]
 }

Vissa filer visades inte eftersom för många filer har ändrats