Replace html5 video tag by video.js component.
This commit is contained in:
Generated
+232
-1
@@ -14,8 +14,10 @@
|
||||
"@angular/forms": "^21.2.0",
|
||||
"@angular/platform-browser": "^21.2.0",
|
||||
"@angular/router": "^21.2.0",
|
||||
"@types/video.js": "^7.3.58",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0"
|
||||
"tslib": "^2.3.0",
|
||||
"video.js": "^8.23.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^21.2.19",
|
||||
@@ -906,6 +908,15 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
|
||||
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
|
||||
@@ -3917,6 +3928,60 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/video.js": {
|
||||
"version": "7.3.58",
|
||||
"resolved": "https://registry.npmjs.org/@types/video.js/-/video.js-7.3.58.tgz",
|
||||
"integrity": "sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@videojs/http-streaming": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-3.17.5.tgz",
|
||||
"integrity": "sha512-PRRQVu0hYW5h0XvRWpIyIpEatpMZzZtf7L1avV89Hnu+69f+0c7oLsSL9jwpg2nbvzGkdX5rQxQkz5Vc7tFR9w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@videojs/vhs-utils": "^4.1.2",
|
||||
"aes-decrypter": "^4.0.2",
|
||||
"global": "^4.4.0",
|
||||
"m3u8-parser": "^7.2.0",
|
||||
"mpd-parser": "^1.4.0",
|
||||
"mux.js": "7.1.0",
|
||||
"video.js": "^7 || ^8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"video.js": "^8.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@videojs/vhs-utils": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-4.1.2.tgz",
|
||||
"integrity": "sha512-1Dsv5dtWOq0LP1G37O46FQFA2tKAZON5UMe6dFPxT7JvPhEx8QIXcFQxan8+hHI3fX6aomFRFrUBJNgRYQDSXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"global": "^4.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/@videojs/xhr": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@videojs/xhr/-/xhr-2.7.0.tgz",
|
||||
"integrity": "sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"global": "~4.4.0",
|
||||
"is-function": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-basic-ssl": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz",
|
||||
@@ -4050,6 +4115,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.13",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
|
||||
"integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@yarnpkg/lockfile": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
|
||||
@@ -4081,6 +4155,18 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/aes-decrypter": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-4.0.2.tgz",
|
||||
"integrity": "sha512-lc+/9s6iJvuaRe5qDlMTpCFjnwpkeOXp8qP3oiZ5jsj1MRg+SBVUmmICrhxHvc8OELSmc+fEyyxAuppY6hrWzw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@videojs/vhs-utils": "^4.1.1",
|
||||
"global": "^4.4.0",
|
||||
"pkcs7": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||
@@ -4871,6 +4957,11 @@
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-walk": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||
},
|
||||
"node_modules/domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||
@@ -5446,6 +5537,16 @@
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/global": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
|
||||
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"min-document": "^2.19.0",
|
||||
"process": "^0.11.10"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
@@ -5728,6 +5829,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-function": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
|
||||
"integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
@@ -6124,6 +6231,17 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/m3u8-parser": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-7.2.0.tgz",
|
||||
"integrity": "sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@videojs/vhs-utils": "^4.1.1",
|
||||
"global": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -6238,6 +6356,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/min-document": {
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz",
|
||||
"integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dom-walk": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
@@ -6387,6 +6514,21 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mpd-parser": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-1.4.0.tgz",
|
||||
"integrity": "sha512-blbA7XpAaOdC/PR0Tu8GiUxlx6CpBuRowQPYV7lhi9Yr9G9+dkPXIUjqjzc/NCn/uZZPopluaJlIYntIEI/VLw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@videojs/vhs-utils": "^4.1.2",
|
||||
"@xmldom/xmldom": "^0.8.3",
|
||||
"global": "^4.4.0"
|
||||
},
|
||||
"bin": {
|
||||
"mpd-to-m3u8-json": "bin/parse.js"
|
||||
}
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||
@@ -6448,6 +6590,23 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mux.js": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-7.1.0.tgz",
|
||||
"integrity": "sha512-NTxawK/BBELJrYsZThEulyUMDVlLizKdxyAsMuzoCD1eFj97BVaA8D/CvKsKu6FOLYkFojN5CbM9h++ZTZtknA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"global": "^4.4.0"
|
||||
},
|
||||
"bin": {
|
||||
"muxjs-transmux": "bin/transmux.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.16",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
||||
@@ -7030,6 +7189,18 @@
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pkcs7": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-1.0.4.tgz",
|
||||
"integrity": "sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5"
|
||||
},
|
||||
"bin": {
|
||||
"pkcs7": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
|
||||
@@ -7119,6 +7290,15 @@
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -8103,6 +8283,57 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/video.js": {
|
||||
"version": "8.23.9",
|
||||
"resolved": "https://registry.npmjs.org/video.js/-/video.js-8.23.9.tgz",
|
||||
"integrity": "sha512-3UCqyFW4wIusQu4TuhgRUxqtbzbj0WPT9hps0XIEXST43QBD11zb+bG7tBPrinrZztwVYdXCiOBo2pcGj9L05w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@videojs/http-streaming": "^3.17.5",
|
||||
"@videojs/vhs-utils": "^4.1.2",
|
||||
"@videojs/xhr": "2.7.0",
|
||||
"aes-decrypter": "^4.0.2",
|
||||
"global": "4.4.0",
|
||||
"m3u8-parser": "^7.2.0",
|
||||
"mpd-parser": "^1.4.0",
|
||||
"mux.js": "^7.0.1",
|
||||
"videojs-contrib-quality-levels": "4.1.0",
|
||||
"videojs-font": "4.2.0",
|
||||
"videojs-vtt.js": "0.15.5"
|
||||
}
|
||||
},
|
||||
"node_modules/videojs-contrib-quality-levels": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.1.0.tgz",
|
||||
"integrity": "sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"global": "^4.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16",
|
||||
"npm": ">=8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"video.js": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/videojs-font": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-4.2.0.tgz",
|
||||
"integrity": "sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/videojs-vtt.js": {
|
||||
"version": "0.15.5",
|
||||
"resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz",
|
||||
"integrity": "sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"global": "^4.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.3.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz",
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
"@angular/forms": "^21.2.0",
|
||||
"@angular/platform-browser": "^21.2.0",
|
||||
"@angular/router": "^21.2.0",
|
||||
"@types/video.js": "^7.3.58",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0"
|
||||
"tslib": "^2.3.0",
|
||||
"video.js": "^8.23.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^21.2.19",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="items-container">
|
||||
@for (item of libraryItems.value(); track item.id) {
|
||||
@if (item.type === 'VIDEO') {
|
||||
<button type="button" (click)="activeVideo.set(item)">
|
||||
<button type="button" (click)="activeVideo.set(item); initializeVideoPlayer(item.id)">
|
||||
[{{item.type[0]}}] ({{item.id.substring(0, 4)}}…) {{item.name}}
|
||||
</button>
|
||||
} @else {
|
||||
@@ -17,8 +17,18 @@
|
||||
</div>
|
||||
<div>
|
||||
@if (activeVideo(); as firstVideo) {
|
||||
<video width="320" height="240" src="/api/videos/{{firstVideo.id}}/stream" controls>
|
||||
Your browser does not support the html video player.
|
||||
<video
|
||||
id="video-player-{{firstVideo.id}}"
|
||||
class="video-js vjs-default-skin"
|
||||
controls
|
||||
preload="auto"
|
||||
width="1024"
|
||||
height="640">
|
||||
<p class="vjs-no-js">
|
||||
To view this video please enable JavaScript, and consider upgrading to a
|
||||
web browser that
|
||||
<a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
|
||||
</p>
|
||||
</video>
|
||||
}
|
||||
</div>
|
||||
|
||||
+47
-2
@@ -1,8 +1,10 @@
|
||||
import {Component, computed, inject, OnInit, resource, signal} from '@angular/core';
|
||||
import {Component, inject, OnInit, resource, signal} from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {firstValueFrom} from 'rxjs';
|
||||
|
||||
import videojs from 'video.js';
|
||||
import type Player from 'video.js/dist/types/player';
|
||||
import 'video.js/dist/video-js.css';
|
||||
enum LibraryItemType {
|
||||
VIDEO = 'VIDEO',
|
||||
FOLDER = 'FOLDER'
|
||||
@@ -30,6 +32,9 @@ export class App implements OnInit {
|
||||
|
||||
activeVideo = signal<LibraryItem | null>(null);
|
||||
|
||||
// Store video.js player instances for cleanup
|
||||
#videoPlayers = new Map<string, Player>();
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadLibrary();
|
||||
}
|
||||
@@ -37,4 +42,44 @@ export class App implements OnInit {
|
||||
loadLibrary(): void {
|
||||
this.libraryItems.reload();
|
||||
}
|
||||
|
||||
// Initialize or update the video player
|
||||
initializeVideoPlayer(videoId: string): void {
|
||||
// Wait for the next tick to ensure DOM is updated
|
||||
setTimeout(() => {
|
||||
const element = document.getElementById('video-player-' + videoId);
|
||||
if (!element) {
|
||||
console.error(`Video element with ID 'video-player-${videoId}' not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up previous player if it exists
|
||||
const previousPlayer = this.#videoPlayers.get(videoId);
|
||||
if (previousPlayer) {
|
||||
previousPlayer.dispose();
|
||||
this.#videoPlayers.delete(videoId);
|
||||
}
|
||||
|
||||
// Initialize new player
|
||||
const player = videojs('video-player-' + videoId, {
|
||||
controls: true,
|
||||
autoplay: false,
|
||||
fluid: true,
|
||||
sources: [{
|
||||
src: `/api/videos/${videoId}/stream`,
|
||||
type: 'video/x-msvideo' // AVI MIME type
|
||||
}]
|
||||
});
|
||||
|
||||
this.#videoPlayers.set(videoId, player);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Clean up all video players on component destroy
|
||||
ngOnDestroy(): void {
|
||||
for (const player of this.#videoPlayers.values()) {
|
||||
player.dispose();
|
||||
}
|
||||
this.#videoPlayers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user