videos werden in .m3u8 beim hochladen gerendert, videojs player hinzugefügt

This commit is contained in:
Chneemann 2024-08-13 11:13:04 +02:00
parent ced191e0fa
commit 8fa95443aa
14 changed files with 720 additions and 88 deletions

View file

@ -8,7 +8,7 @@ class Video(models.Model):
description = models.CharField(max_length=500) description = models.CharField(max_length=500)
film_genre = models.CharField(max_length=20, choices=FILM_GENRES, blank=True, null=True) film_genre = models.CharField(max_length=20, choices=FILM_GENRES, blank=True, null=True)
video_file = models.FileField(upload_to='videos/', blank=True, null=True) video_file = models.FileField(upload_to='videos/', blank=True, null=True)
thumbnail = models.ImageField(upload_to='thumbnails/', blank=True, null=True) file_name = models.CharField(max_length=50, blank=True, null=True)
def __str__(self): def __str__(self):
return self.title return f'({self.id}) {self.title}'

View file

@ -1,10 +1,11 @@
from .models import Video from .models import Video
from content.tasks import convert_video, create_thumbnails from .tasks import convert_video_to_hls, create_thumbnails, delete_original_video
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save, post_delete
from django.conf import settings from django.conf import settings
import os import os
import django_rq import django_rq
import shutil
@receiver(post_save, sender=Video) @receiver(post_save, sender=Video)
def video_post_save(sender, instance, created, **kwargs): def video_post_save(sender, instance, created, **kwargs):
@ -14,40 +15,35 @@ def video_post_save(sender, instance, created, **kwargs):
if created: if created:
queue = django_rq.get_queue("default", autocommit=True) queue = django_rq.get_queue("default", autocommit=True)
#Convert Video #Convert video
queue.enqueue(convert_video, instance.video_file.path, "480") queue.enqueue(convert_video_to_hls, instance.video_file.path, "480", instance.id)
queue.enqueue(convert_video, instance.video_file.path, "720") queue.enqueue(convert_video_to_hls, instance.video_file.path, "720", instance.id)
queue.enqueue(convert_video, instance.video_file.path, "1080") queue.enqueue(convert_video_to_hls, instance.video_file.path, "1080", instance.id)
#Create Thumbnail #Create thumbnail
queue.enqueue(create_thumbnails, instance) queue.enqueue(create_thumbnails, instance, instance.id)
# Delete the original video file
queue.enqueue(delete_original_video, instance.video_file.path)
@receiver(post_delete, sender=Video) @receiver(post_delete, sender=Video)
def auto_delete_file_on_delete(sender, instance, **kwargs): def auto_delete_file_on_delete(sender, instance, **kwargs):
""" """
Deletes video file and its converted files from filesystem Deletes the video and all converted files from the file system,
when corresponding `Video` object is deleted. when the corresponding `Video` object is deleted.
""" """
# Delete the original video file main_directory = os.path.dirname(instance.video_file.path)
if instance.video_file: model_directory = os.path.join(main_directory, str(instance.id))
if os.path.isfile(instance.video_file.path): delete_directory(model_directory)
os.remove(instance.video_file.path)
# Delete the converted videos def delete_directory(directory_path):
if instance.video_file: """
delete_converted_files(instance.video_file.path) Deletes the specified directory recursively.
"""
# Delete the thumbnails if os.path.exists(directory_path):
if instance.thumbnail: shutil.rmtree(directory_path)
thumbnail_dir = settings.THUMBNAIL_DIR else:
base_filename = os.path.splitext(os.path.basename(instance.video_file.path))[0] print(f"Folder not found: {directory_path}")
thumbnail_1080p_path = os.path.join(thumbnail_dir, base_filename + '_1080p.jpg')
thumbnail_480_path = os.path.join(thumbnail_dir, base_filename + '_480p.jpg')
if os.path.isfile(thumbnail_1080p_path):
os.remove(thumbnail_1080p_path)
if os.path.isfile(thumbnail_480_path):
os.remove(thumbnail_480_path)
def remove_first_mp4(filename): def remove_first_mp4(filename):
""" """

View file

@ -1,30 +1,69 @@
import re
import subprocess import subprocess
import ffmpeg import ffmpeg
import os import os
from django.conf import settings from django.conf import settings
from .models import Video
def convert_video_to_hls(source, resolution, model_id):
target_dir = os.path.join(os.path.dirname(source), str(model_id))
os.makedirs(target_dir, exist_ok=True)
base_filename = os.path.basename(source).split(".")[0]
target = os.path.join(target_dir, f'{base_filename}_{resolution}p')
def convert_video(source, resolution):
target = source + f'_{resolution}p.mp4'
target = remove_first_mp4(target)
ffmpeg_path = '/opt/homebrew/bin/ffmpeg' ffmpeg_path = '/opt/homebrew/bin/ffmpeg'
cmd = [ffmpeg_path, '-i', source, '-s', f'hd{resolution}', '-c:v', 'libx264', '-crf', '23', '-c:a', 'aac', '-strict', '-2', target] cmd = [
ffmpeg_path,
'-i', source,
'-s', f'hd{resolution}',
'-c:v', 'libx264',
'-crf', '23',
'-c:a', 'aac',
'-strict', '-2',
'-hls_time', '10',
'-hls_playlist_type', 'vod',
'-hls_segment_filename', os.path.join(target_dir, f'{base_filename}_{resolution}p_%03d.ts'),
f'{target}.m3u8'
]
result = subprocess.run(cmd, capture_output=True, text=True) result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
save_converted_file_name(model_id, base_filename)
else:
print(f"Conversion error: {result.stderr}")
def remove_first_mp4(filename): def save_converted_file_name(model_id, file_name):
name_part, ext_part = filename.split('.mp4', 1) """
new_filename = name_part + ext_part Saves the name of the converted file in the model or a database.
return new_filename """
try:
instance = Video.objects.get(id=model_id)
instance.file_name = file_name
instance.save()
except Video.DoesNotExist:
print(f"Model with ID {model_id} not found.")
except Exception as e:
print(f"Error saving file name:: {e}")
def create_thumbnails(self): def delete_original_video(source):
"""
Deletes the original MP4 file.
"""
try:
os.remove(source)
except OSError as e:
print(f"Error deleting original file: {e}")
def create_thumbnails(self, model_id):
video_file_path = self.video_file.path video_file_path = self.video_file.path
thumbnail_dir = settings.THUMBNAIL_DIR thumbnail_dir = os.path.join(settings.THUMBNAIL_DIR, str(model_id))
base_filename = os.path.splitext(os.path.basename(video_file_path))[0] base_filename = os.path.splitext(os.path.basename(video_file_path))[0]
thumbnail_1080p_filename = base_filename + '_1080p.jpg' thumbnail_1080p_filename = base_filename + '_1080p.jpg'
thumbnail_1080p_path = os.path.join(thumbnail_dir, thumbnail_1080p_filename) thumbnail_1080p_path = os.path.join(thumbnail_dir, thumbnail_1080p_filename)
thumbnail_480_filename = base_filename + '_480p.jpg' thumbnail_480_filename = base_filename + '_480p.jpg'
thumbnail_480_path = os.path.join(thumbnail_dir, thumbnail_480_filename) thumbnail_480_path = os.path.join(thumbnail_dir, thumbnail_480_filename)
thumbnail_path = os.path.join(thumbnail_dir, base_filename + ".jpg")
if not os.path.exists(thumbnail_dir): if not os.path.exists(thumbnail_dir):
os.makedirs(thumbnail_dir) os.makedirs(thumbnail_dir)
@ -33,9 +72,5 @@ def create_thumbnails(self):
ffmpeg.input(video_file_path, ss=1).output(thumbnail_1080p_path, vf='scale=1920:-1', vframes=1).run(overwrite_output=True) ffmpeg.input(video_file_path, ss=1).output(thumbnail_1080p_path, vf='scale=1920:-1', vframes=1).run(overwrite_output=True)
ffmpeg.input(video_file_path, ss=1).output(thumbnail_480_path, vf='scale=720:-1', vframes=1).run(overwrite_output=True) ffmpeg.input(video_file_path, ss=1).output(thumbnail_480_path, vf='scale=720:-1', vframes=1).run(overwrite_output=True)
# Hier speichern wir nur das Thumbnail für die Vorschau im Modell
self.thumbnail = os.path.relpath(thumbnail_path, start=settings.MEDIA_ROOT)
self.save()
except ffmpeg._run.Error as e: except ffmpeg._run.Error as e:
print(f"Ein Fehler ist aufgetreten: {e.stderr.decode()}") print(f"Ein Fehler ist aufgetreten: {e.stderr.decode()}")

View file

@ -20,18 +20,11 @@
"outputPath": "dist/frontend", "outputPath": "dist/frontend",
"index": "src/index.html", "index": "src/index.html",
"browser": "src/main.ts", "browser": "src/main.ts",
"polyfills": [ "polyfills": ["zone.js"],
"zone.js"
],
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": [ "assets": ["src/favicon.ico", "src/assets"],
"src/favicon.ico", "styles": ["src/styles.scss"],
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": [] "scripts": []
}, },
"configurations": { "configurations": {
@ -79,20 +72,18 @@
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"polyfills": [ "polyfills": ["zone.js", "zone.js/testing"],
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": [ "assets": ["src/favicon.ico", "src/assets"],
"src/favicon.ico",
"src/assets"
],
"styles": [ "styles": [
"src/styles.scss" "src/styles.scss",
"node_modules/video.js/dist/video-js.css"
], ],
"scripts": [] "scripts": [
"node_modules/video.js/dist/video.min.js",
"node_modules/videojs-contrib-hls/dist/videojs-contrib-hls.js"
]
} }
} }
} }

View file

@ -18,6 +18,8 @@
"@angular/router": "^17.2.0", "@angular/router": "^17.2.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"video.js": "^8.17.2",
"videojs-contrib-hls": "^5.15.0",
"zone.js": "~0.14.3" "zone.js": "~0.14.3"
}, },
"devDependencies": { "devDependencies": {
@ -25,6 +27,7 @@
"@angular/cli": "^17.2.1", "@angular/cli": "^17.2.1",
"@angular/compiler-cli": "^17.2.0", "@angular/compiler-cli": "^17.2.0",
"@types/jasmine": "~5.1.0", "@types/jasmine": "~5.1.0",
"@types/video.js": "^7.3.58",
"jasmine-core": "~5.1.0", "jasmine-core": "~5.1.0",
"karma": "~6.4.0", "karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",
@ -2220,7 +2223,6 @@
"version": "7.24.0", "version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
"dev": true,
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
}, },
@ -3786,6 +3788,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"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==",
"dev": true
},
"node_modules/@types/ws": { "node_modules/@types/ws": {
"version": "8.5.12", "version": "8.5.12",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
@ -3795,6 +3803,52 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@videojs/http-streaming": {
"version": "3.13.2",
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-3.13.2.tgz",
"integrity": "sha512-eCfQp61w00hg7Y9npmLnsJ6UvDF+SsFYzu7mQJgVXxWpVm9AthYWA3KQEKA7F7Sy6yzlm/Sps8BHs5ItelNZgQ==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "4.0.0",
"aes-decrypter": "4.0.1",
"global": "^4.4.0",
"m3u8-parser": "^7.1.0",
"mpd-parser": "^1.3.0",
"mux.js": "7.0.3",
"video.js": "^7 || ^8"
},
"engines": {
"node": ">=8",
"npm": ">=5"
},
"peerDependencies": {
"video.js": "^8.14.0"
}
},
"node_modules/@videojs/vhs-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-4.0.0.tgz",
"integrity": "sha512-xJp7Yd4jMLwje2vHCUmi8MOUU76nxiwII3z4Eg3Ucb+6rrkFVGosrXlMgGnaLjq724j3wzNElRZ71D/CKrTtxg==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"global": "^4.4.0",
"url-toolkit": "^2.2.1"
},
"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==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"global": "~4.4.0",
"is-function": "^1.0.1"
}
},
"node_modules/@vitejs/plugin-basic-ssl": { "node_modules/@vitejs/plugin-basic-ssl": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz",
@ -3953,6 +4007,14 @@
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"node_modules/@xmldom/xmldom": {
"version": "0.8.10",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/@xtuc/ieee754": { "node_modules/@xtuc/ieee754": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@ -4041,6 +4103,31 @@
"node": ">=8.9.0" "node": ">=8.9.0"
} }
}, },
"node_modules/aes-decrypter": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-4.0.1.tgz",
"integrity": "sha512-H1nh/P9VZXUf17AA5NQfJML88CFjVBDuGkp5zDHa7oEhYN9TTpNLJknRY1ie0iSKWlDf6JRnJKaZVDSQdPy6Cg==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "^3.0.5",
"global": "^4.4.0",
"pkcs7": "^1.0.4"
}
},
"node_modules/aes-decrypter/node_modules/@videojs/vhs-utils": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz",
"integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"global": "^4.4.0",
"url-toolkit": "^2.2.1"
},
"engines": {
"node": ">=8",
"npm": ">=5"
}
},
"node_modules/agent-base": { "node_modules/agent-base": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
@ -4358,6 +4445,20 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
} }
}, },
"node_modules/babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
"dependencies": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
}
},
"node_modules/babel-runtime/node_modules/regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -5128,6 +5229,13 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true
},
"node_modules/core-js-compat": { "node_modules/core-js-compat": {
"version": "3.37.1", "version": "3.37.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz",
@ -5556,6 +5664,11 @@
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" "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": { "node_modules/domelementtype": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
@ -5793,6 +5906,14 @@
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
"dev": true "dev": true
}, },
"node_modules/es5-shim": {
"version": "4.6.7",
"resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.7.tgz",
"integrity": "sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.20.1", "version": "0.20.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz",
@ -6501,6 +6622,15 @@
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"dev": true "dev": true
}, },
"node_modules/global": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
"dependencies": {
"min-document": "^2.19.0",
"process": "^0.11.10"
}
},
"node_modules/globals": { "node_modules/globals": {
"version": "11.12.0", "version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@ -6978,6 +7108,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/individual": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz",
"integrity": "sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g=="
},
"node_modules/inflight": { "node_modules/inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -7136,6 +7271,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"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=="
},
"node_modules/is-glob": { "node_modules/is-glob": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@ -8040,6 +8180,30 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/m3u8-parser": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-7.1.0.tgz",
"integrity": "sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "^3.0.5",
"global": "^4.4.0"
}
},
"node_modules/m3u8-parser/node_modules/@videojs/vhs-utils": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz",
"integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"global": "^4.4.0",
"url-toolkit": "^2.2.1"
},
"engines": {
"node": ">=8",
"npm": ">=5"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.8", "version": "0.30.8",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
@ -8217,6 +8381,14 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
"dependencies": {
"dom-walk": "^0.1.0"
}
},
"node_modules/mini-css-extract-plugin": { "node_modules/mini-css-extract-plugin": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz",
@ -8463,6 +8635,20 @@
"mkdirp": "bin/cmd.js" "mkdirp": "bin/cmd.js"
} }
}, },
"node_modules/mpd-parser": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-1.3.0.tgz",
"integrity": "sha512-WgeIwxAqkmb9uTn4ClicXpEQYCEduDqRKfmUdp4X8vmghKfBNXZLYpREn9eqrDx/Tf5LhzRcJLSpi4ohfV742Q==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "^4.0.0",
"@xmldom/xmldom": "^0.8.3",
"global": "^4.4.0"
},
"bin": {
"mpd-to-m3u8-json": "bin/parse.js"
}
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
@ -8500,6 +8686,22 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
} }
}, },
"node_modules/mux.js": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-7.0.3.tgz",
"integrity": "sha512-gzlzJVEGFYPtl2vvEiJneSWAWD4nfYRHD5XgxmB2gWvXraMPOYk+sxfvexmNfjQUFpmk6hwLR5C6iSFmuwCHdQ==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"global": "^4.4.0"
},
"bin": {
"muxjs-transmux": "bin/transmux.js"
},
"engines": {
"node": ">=8",
"npm": ">=5"
}
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.7", "version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@ -9201,6 +9403,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/parse-headers": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz",
"integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="
},
"node_modules/parse-json": { "node_modules/parse-json": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -9388,6 +9595,17 @@
"nice-napi": "^1.0.2" "nice-napi": "^1.0.2"
} }
}, },
"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==",
"dependencies": {
"@babel/runtime": "^7.5.5"
},
"bin": {
"pkcs7": "bin/cli.js"
}
},
"node_modules/pkg-dir": { "node_modules/pkg-dir": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz",
@ -9625,6 +9843,14 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.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==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": { "node_modules/process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -9900,8 +10126,7 @@
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.14.1", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
"dev": true
}, },
"node_modules/regenerator-transform": { "node_modules/regenerator-transform": {
"version": "0.15.2", "version": "0.15.2",
@ -10166,6 +10391,14 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"node_modules/rust-result": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/rust-result/-/rust-result-1.0.0.tgz",
"integrity": "sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==",
"dependencies": {
"individual": "^2.0.0"
}
},
"node_modules/rxjs": { "node_modules/rxjs": {
"version": "7.8.1", "version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
@ -10194,6 +10427,14 @@
} }
] ]
}, },
"node_modules/safe-json-parse": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-4.0.0.tgz",
"integrity": "sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==",
"dependencies": {
"rust-result": "^1.0.0"
}
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@ -11233,6 +11474,12 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
}, },
"node_modules/tsml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tsml/-/tsml-1.0.1.tgz",
"integrity": "sha512-3KmepnH9SUsoOVtg013CRrL7c+AK7ECaquAsJdvu4288EDJuraqBlP4PDXT/rLEJ9YDn4jqLAzRJsnFPx+V6lg==",
"deprecated": "no longer maintained"
},
"node_modules/tuf-js": { "node_modules/tuf-js": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz",
@ -11459,6 +11706,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/url-toolkit": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.5.tgz",
"integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg=="
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -11511,6 +11763,216 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/video.js": {
"version": "8.17.2",
"resolved": "https://registry.npmjs.org/video.js/-/video.js-8.17.2.tgz",
"integrity": "sha512-oa4BGAr5H965OBcn83qM9xMMtjtSCRh0zMLnyouD9itQJ994FY/NlYo+XSPujk4NpsBGHSUF/+rGy0Wu5Mrzqg==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@videojs/http-streaming": "3.13.2",
"@videojs/vhs-utils": "^4.0.0",
"@videojs/xhr": "2.7.0",
"aes-decrypter": "^4.0.1",
"global": "4.4.0",
"m3u8-parser": "^7.1.0",
"mpd-parser": "^1.2.2",
"mux.js": "^7.0.1",
"safe-json-parse": "4.0.0",
"videojs-contrib-quality-levels": "4.1.0",
"videojs-font": "4.2.0",
"videojs-vtt.js": "0.15.5"
}
},
"node_modules/videojs-contrib-hls": {
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/videojs-contrib-hls/-/videojs-contrib-hls-5.15.0.tgz",
"integrity": "sha512-18zbMYZ0XRBKTPEayA9bFTWWrqhT9b4G8+zf0czJLD7Epe5PcK1I/3dflTHQeQ5rwlWir+/XnFU3sMg/B2MMcw==",
"dependencies": {
"aes-decrypter": "1.0.3",
"global": "^4.3.0",
"m3u8-parser": "2.1.0",
"mux.js": "4.3.2",
"url-toolkit": "^2.1.3",
"video.js": "^5.19.1 || ^6.2.0",
"videojs-contrib-media-sources": "4.7.2",
"webwackify": "0.1.6"
},
"engines": {
"node": ">= 0.10.12"
}
},
"node_modules/videojs-contrib-hls/node_modules/aes-decrypter": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-1.0.3.tgz",
"integrity": "sha512-rsx8pfx7wJsn+ziYbpJ8XA5c93hKAtBCrfydxJqJCMT+qfjipd/B5wC2xHtBcoxyvlqJcpeAo3K55t0lXOn9yQ==",
"dependencies": {
"pkcs7": "^0.2.3"
}
},
"node_modules/videojs-contrib-hls/node_modules/global": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
"integrity": "sha512-/4AybdwIDU4HkCUbJkZdWpe4P6vuw/CUtu+0I1YlLIPe7OlUO7KNJ+q/rO70CW2/NW6Jc6I62++Hzsf5Alu6rQ==",
"dependencies": {
"min-document": "^2.19.0",
"process": "~0.5.1"
}
},
"node_modules/videojs-contrib-hls/node_modules/m3u8-parser": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-2.1.0.tgz",
"integrity": "sha512-WbEpQ2FUaNGbJ0YanSeyj9D9ruu4FUvz+ZvebIzI2bSME+PUwcPXO1kKXZkjcPUAFruDikoOI5fWQNIA6JCCOQ=="
},
"node_modules/videojs-contrib-hls/node_modules/mux.js": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-4.3.2.tgz",
"integrity": "sha512-g0q6DPdvb3yYcoK7ElBGobdSSrhY/RjPt19U7uUc733aqvc5bCS/aCvL9z+448y+IoCZnYDwyZfQBBXMSmGOaQ=="
},
"node_modules/videojs-contrib-hls/node_modules/pkcs7": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-0.2.3.tgz",
"integrity": "sha512-kJRwmADEQUg+qJyRgWLtpEL9q9cFjZschejTEK3GRjKvnsU9G5WWoe/wKqRgbBoqWdVSeTUKP6vIA3Y72M3rWA==",
"bin": {
"pkcs7": "lib/cli.js"
},
"engines": {
"node": "^0.10",
"npm": "^1.4.6"
}
},
"node_modules/videojs-contrib-hls/node_modules/process": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
"integrity": "sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/videojs-contrib-hls/node_modules/video.js": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/video.js/-/video.js-6.13.0.tgz",
"integrity": "sha512-36/JR/GhPQSZj0o+GNbhcEYv/b0SkV9SQsjlodAnzMQYN0TA7VhmqrKPYMCi1NGRYu7S9W3OaFCFoUxkYfSVlg==",
"dependencies": {
"babel-runtime": "^6.9.2",
"global": "4.3.2",
"safe-json-parse": "4.0.0",
"tsml": "1.0.1",
"videojs-font": "2.1.0",
"videojs-ie8": "1.1.2",
"videojs-vtt.js": "0.12.6",
"xhr": "2.4.0"
}
},
"node_modules/videojs-contrib-hls/node_modules/videojs-font": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-2.1.0.tgz",
"integrity": "sha512-zFqWpLrXf1q8NtYx5qtZhMC6SLUFScDmR6j+UGPogobxR21lvXShhnzcNNMdOxJUuFLiToJ/BPpFUQwX4xhpvA=="
},
"node_modules/videojs-contrib-hls/node_modules/videojs-vtt.js": {
"version": "0.12.6",
"resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.12.6.tgz",
"integrity": "sha512-XFXeGBQiljnElMhwCcZst0RDbZn2n8LU7ZScXryd3a00OaZsHAjdZu/7/RdSr7Z1jHphd45FnOvOQkGK4YrWCQ==",
"dependencies": {
"global": "^4.3.1"
}
},
"node_modules/videojs-contrib-media-sources": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/videojs-contrib-media-sources/-/videojs-contrib-media-sources-4.7.2.tgz",
"integrity": "sha512-e6iCHWBFuV05EGo7v+pS9iepObXnJ9joms467gzi8ZjpKVb3ifha9M0Ja24Rd8JfvYpzjltsgDVtGFDvIg4hQQ==",
"dependencies": {
"global": "^4.3.0",
"mux.js": "4.3.2",
"video.js": "^5.17.0 || ^6.2.0",
"webwackify": "0.1.6"
}
},
"node_modules/videojs-contrib-media-sources/node_modules/global": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
"integrity": "sha512-/4AybdwIDU4HkCUbJkZdWpe4P6vuw/CUtu+0I1YlLIPe7OlUO7KNJ+q/rO70CW2/NW6Jc6I62++Hzsf5Alu6rQ==",
"dependencies": {
"min-document": "^2.19.0",
"process": "~0.5.1"
}
},
"node_modules/videojs-contrib-media-sources/node_modules/mux.js": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-4.3.2.tgz",
"integrity": "sha512-g0q6DPdvb3yYcoK7ElBGobdSSrhY/RjPt19U7uUc733aqvc5bCS/aCvL9z+448y+IoCZnYDwyZfQBBXMSmGOaQ=="
},
"node_modules/videojs-contrib-media-sources/node_modules/process": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
"integrity": "sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/videojs-contrib-media-sources/node_modules/video.js": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/video.js/-/video.js-6.13.0.tgz",
"integrity": "sha512-36/JR/GhPQSZj0o+GNbhcEYv/b0SkV9SQsjlodAnzMQYN0TA7VhmqrKPYMCi1NGRYu7S9W3OaFCFoUxkYfSVlg==",
"dependencies": {
"babel-runtime": "^6.9.2",
"global": "4.3.2",
"safe-json-parse": "4.0.0",
"tsml": "1.0.1",
"videojs-font": "2.1.0",
"videojs-ie8": "1.1.2",
"videojs-vtt.js": "0.12.6",
"xhr": "2.4.0"
}
},
"node_modules/videojs-contrib-media-sources/node_modules/videojs-font": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-2.1.0.tgz",
"integrity": "sha512-zFqWpLrXf1q8NtYx5qtZhMC6SLUFScDmR6j+UGPogobxR21lvXShhnzcNNMdOxJUuFLiToJ/BPpFUQwX4xhpvA=="
},
"node_modules/videojs-contrib-media-sources/node_modules/videojs-vtt.js": {
"version": "0.12.6",
"resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.12.6.tgz",
"integrity": "sha512-XFXeGBQiljnElMhwCcZst0RDbZn2n8LU7ZScXryd3a00OaZsHAjdZu/7/RdSr7Z1jHphd45FnOvOQkGK4YrWCQ==",
"dependencies": {
"global": "^4.3.1"
}
},
"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==",
"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=="
},
"node_modules/videojs-ie8": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/videojs-ie8/-/videojs-ie8-1.1.2.tgz",
"integrity": "sha512-0Zb2T4MLkpfZbeGMK/Z93b8Lrepr+rLFoHgQV1CoDeFqXvH7b+Vsd/VHoILGxQrgCSHFQ7mAODR6oyMjuiD4/g==",
"dependencies": {
"es5-shim": "^4.5.1"
}
},
"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==",
"dependencies": {
"global": "^4.3.1"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "5.1.7", "version": "5.1.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz",
@ -12291,6 +12753,11 @@
"node": ">=0.8.0" "node": ">=0.8.0"
} }
}, },
"node_modules/webwackify": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/webwackify/-/webwackify-0.1.6.tgz",
"integrity": "sha512-pGcw1T3HpNnM/UTRQqqRkkkzythSLts05mB+7Gr00B+0VbL0m39dFL5g20rSIEUt9Wrpw+/8k+snxRlUFHhcqA=="
},
"node_modules/which": { "node_modules/which": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@ -12434,6 +12901,42 @@
} }
} }
}, },
"node_modules/xhr": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/xhr/-/xhr-2.4.0.tgz",
"integrity": "sha512-TUbBsdAuJbX8olk9hsDwGK8P1ri1XlV+PdEWkYw+HQQbpkiBR8PLgD1F3kQDPBs9l4Px34hP9rCYAZOCCAENbw==",
"dependencies": {
"global": "~4.3.0",
"is-function": "^1.0.1",
"parse-headers": "^2.0.0",
"xtend": "^4.0.0"
}
},
"node_modules/xhr/node_modules/global": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
"integrity": "sha512-/4AybdwIDU4HkCUbJkZdWpe4P6vuw/CUtu+0I1YlLIPe7OlUO7KNJ+q/rO70CW2/NW6Jc6I62++Hzsf5Alu6rQ==",
"dependencies": {
"min-document": "^2.19.0",
"process": "~0.5.1"
}
},
"node_modules/xhr/node_modules/process": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
"integrity": "sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View file

@ -20,6 +20,8 @@
"@angular/router": "^17.2.0", "@angular/router": "^17.2.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"video.js": "^8.17.2",
"videojs-contrib-hls": "^5.15.0",
"zone.js": "~0.14.3" "zone.js": "~0.14.3"
}, },
"devDependencies": { "devDependencies": {
@ -27,6 +29,7 @@
"@angular/cli": "^17.2.1", "@angular/cli": "^17.2.1",
"@angular/compiler-cli": "^17.2.0", "@angular/compiler-cli": "^17.2.0",
"@types/jasmine": "~5.1.0", "@types/jasmine": "~5.1.0",
"@types/video.js": "^7.3.58",
"jasmine-core": "~5.1.0", "jasmine-core": "~5.1.0",
"karma": "~6.4.0", "karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",

View file

@ -20,13 +20,11 @@
<img src="./../../../../assets/img/logo_ci.svg" alt="Logo" /> <img src="./../../../../assets/img/logo_ci.svg" alt="Logo" />
</div> </div>
</div> </div>
<video width="100%" height="100%" controls autoplay> <app-video-player [playMovie]="playMovie"></app-video-player>
<source <!-- <video width="100%" height="100%" controls autoplay>
src="{{ playMovie.replace('.mp4', '_1080p.mp4') }}" <source src="{{ playMovie }}" type="video/mp4" />
type="video/mp4"
/>
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video> -->
</div> </div>
} }
</section> </section>

View file

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { HeaderComponent } from '../../../shared/components/header/header.component'; import { HeaderComponent } from '../../../shared/components/header/header.component';
import { HeroBannerComponent } from './hero-banner/hero-banner.component'; import { HeroBannerComponent } from './hero-banner/hero-banner.component';
import { CategoriesComponent } from './categories/categories.component'; import { CategoriesComponent } from './categories/categories.component';
@ -6,6 +6,7 @@ import { AuthService } from '../../../services/auth.service';
import { MovieService } from '../../../services/movie.service'; import { MovieService } from '../../../services/movie.service';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { VideoPlayerComponent } from './vjs-player/vjs-player.component';
@Component({ @Component({
selector: 'app-browse', selector: 'app-browse',
@ -15,6 +16,7 @@ import { CommonModule } from '@angular/common';
HeaderComponent, HeaderComponent,
HeroBannerComponent, HeroBannerComponent,
CategoriesComponent, CategoriesComponent,
VideoPlayerComponent,
], ],
templateUrl: './browse.component.html', templateUrl: './browse.component.html',
styleUrl: './browse.component.scss', styleUrl: './browse.component.scss',

View file

@ -8,8 +8,13 @@
<img <img
[ngClass]="{ selected: movie.id === currentMovie }" [ngClass]="{ selected: movie.id === currentMovie }"
src="{{ src="{{
environmentBaseUrl + movie.thumbnail.replace('.jpg', '_480p.jpg') environmentBaseUrl +
}} }}" '/media/thumbnails/' +
movie.id +
'/' +
movie.file_name +
'_480p.jpg'
}}"
alt="" alt=""
/> />
</div> </div>
@ -28,7 +33,12 @@
<img <img
[ngClass]="{ selected: movie.id === currentMovie }" [ngClass]="{ selected: movie.id === currentMovie }"
src="{{ src="{{
environmentBaseUrl + movie.thumbnail.replace('.jpg', '_480p.jpg') environmentBaseUrl +
'/media/thumbnails/' +
movie.id +
'/' +
movie.file_name +
'_480p.jpg'
}}" }}"
alt="" alt=""
/> />

View file

@ -4,21 +4,34 @@
'background-image': 'background-image':
'url(' + 'url(' +
environmentBaseUrl + environmentBaseUrl +
currentMovie[0]?.thumbnail.replace('.jpg', '_1080p.jpg') + '/media/thumbnails/' +
currentMovie[0]?.id +
'/' +
currentMovie[0]?.file_name +
'_1080p.jpg' +
')' ')'
}" }"
> >
@if (currentMovie.length > 0) { @if (currentMovie.length > 0) {
<div class="content"> <div class="content">
<div class="title">{{ currentMovie[0]?.title }}</div> <div class="title">{{ currentMovie[0].title }}</div>
<div class="description"> <div class="description">
{{ currentMovie[0]?.description }} {{ currentMovie[0].description }}
</div> </div>
<app-btn-large <app-btn-large
[value]="'Play'" [value]="'Play'"
[imgPath]="'play'" [imgPath]="'play'"
[imgReverse]="true" [imgReverse]="true"
(click)="playMovieId(environmentBaseUrl + currentMovie[0]?.video_file)" (click)="
playMovieId(
environmentBaseUrl +
'/media/videos/' +
currentMovie[0]?.id +
'/' +
currentMovie[0]?.file_name +
'_1080p.m3u8'
)
"
></app-btn-large> ></app-btn-large>
</div> </div>
} }

View file

@ -0,0 +1,3 @@
<video id="my-video" class="video-js vjs-default-skin">
<source src="{{ playMovie }}" type="application/x-mpegURL" />
</video>

View file

@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { VideoPlayerComponent } from './vjs-player.component';
describe('VjsPlayerComponent', () => {
let component: VideoPlayerComponent;
let fixture: ComponentFixture<VideoPlayerComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [VideoPlayerComponent],
}).compileComponents();
fixture = TestBed.createComponent(VideoPlayerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,56 @@
import {
Component,
OnInit,
OnDestroy,
ElementRef,
Input,
HostListener,
} from '@angular/core';
import videojs from 'video.js';
import log from 'video.js/dist/types/utils/log';
@Component({
selector: 'app-video-player',
standalone: true,
templateUrl: './vjs-player.component.html',
styleUrls: ['./vjs-player.component.scss'],
})
export class VideoPlayerComponent implements OnInit, OnDestroy {
@Input() playMovie: string = '';
private player: any;
private screenWidth: number = 0;
private screenHeight: number = 0;
constructor(private elementRef: ElementRef) {}
ngOnInit(): void {
this.updateScreenDimensions();
this.player = videojs(
this.elementRef.nativeElement.querySelector('video'),
{
controls: true,
autoplay: true,
height: this.screenHeight,
width: this.screenWidth,
preload: 'auto',
sources: [
{
src: this.playMovie,
type: 'application/x-mpegURL',
},
],
}
);
}
private updateScreenDimensions() {
this.screenWidth = window.innerWidth;
this.screenHeight = window.innerHeight;
}
ngOnDestroy(): void {
if (this.player) {
this.player.dispose();
}
}
}