diff --git a/README.md b/README.md index 1562d41..1ec5188 100644 --- a/README.md +++ b/README.md @@ -55,24 +55,31 @@ $localVideo = new Video('/pfad/zum/video.mp4', 'Eigenes Video'); echo $localVideo->generate(); ``` -## 🛠 Die Methoden +Gerne, hier ist die ergänzte Version der Methodenübersicht mit Rückgabetypen: + +## 🛠 Die Class ### Konstruktor ```php -__construct($source, $title = '', $lang = 'de') +__construct($source, $title = '', $lang = 'de'): void ``` - `$source`: URL oder Pfad zum Video (Pflicht) - `$title`: Titel des Videos (Optional) - `$lang`: Sprachcode (Optional, Standard: 'de') -### Weitere Methoden +### Methoden +- `setAttributes(array $attributes): void`: Zusätzliche Player-Attribute +- `setA11yContent($description, $alternativeUrl = ''): void`: Barrierefreiheits-Infos +- `setThumbnails($thumbnailsUrl): void`: Thumbnail-Vorschaubilder (VTT-Format) +- `addSubtitle($src, $kind, $label, $lang, $default = false): void`: Untertitel hinzufügen +- `generateFull(): string`: Vollständiger HTML-Code mit allen Schikanen +- `generate(): string`: Einfacher Video-Player ohne Schnickschnack +- `isMedia($url): bool`: Prüft, ob es sich um eine Mediendatei handelt +- `isAudio($url): bool`: Prüft, ob es sich um eine Audiodatei handelt +- `videoOembedHelper(): void`: Registriert einen Output-Filter für oEmbed-Tags +- `parseOembedTags(string $content): string`: Parst oEmbed-Tags im Inhalt +- `show_sidebar(\rex_extension_point $ep): ?string`: Generiert Medienvorschau für die Sidebar im Medienpool -- `setAttributes(array $attributes)`: Zusätzliche Player-Attribute -- `setA11yContent($description, $alternativeUrl = '')`: Barrierefreiheits-Infos -- `setThumbnails($thumbnailsUrl)`: Thumbnail-Vorschaubilder (VTT-Format) -- `addSubtitle($src, $kind, $label, $lang, $default = false)`: Untertitel hinzufügen -- `generateFull()`: Vollständiger HTML-Code mit allen Schikanen -- `generate()`: Einfacher Video-Player ohne Schnickschnack ## 📋 Optionen und Pflichtangaben @@ -224,6 +231,15 @@ $easyVideo = createDefaultVideo('https://youtube.com/watch?v=abcdefg', 'Einfach echo $easyVideo->generateFull(); ``` +## 🎸 Unterstützung für Audio-Dateien + +Das Addon unterstützt auch die Einbindung von Audio-Dateien. Genauso wie für Videos: + +```php +$audio = new Video('audio.mp3', 'Mein Lieblingssong'); +echo $audio->generate(); +``` + ## ✔︎ Im Backend schon integriert Hier muss man nichts machen - außer Videos schauen. diff --git a/assets/vidstack_helper.js b/assets/vidstack_helper.js index a01764b..417ac45 100644 --- a/assets/vidstack_helper.js +++ b/assets/vidstack_helper.js @@ -1,13 +1,11 @@ -(function() { +(function () { let translations = {}; async function loadTranslations() { try { - const response = await fetch('/assets/addons/vidstack/translations.json'); - translations = await response.json(); + translations = await (await fetch('/assets/addons/vidstack/translations.json')).json(); } catch (error) { console.error('Fehler beim Laden der Übersetzungen:', error); - // Fallback zu leeren Übersetzungen, wenn das Laden fehlschlägt translations = { de: {}, en: {} }; } } @@ -16,124 +14,107 @@ return (translations[lang] && translations[lang][key]) || key; } - async function initializeScript() { - await loadTranslations(); - - const consentKey = 'video_consent'; - let videoConsent = JSON.parse(localStorage.getItem(consentKey) || '{}'); + const consentKey = 'video_consent'; + let videoConsent = JSON.parse(localStorage.getItem(consentKey) || '{}'); - function setConsent(platform) { - videoConsent[platform] = true; - localStorage.setItem(consentKey, JSON.stringify(videoConsent)); - document.cookie = `${platform}_consent=true; path=/; max-age=${30 * 24 * 60 * 60}; SameSite=Lax; Secure`; - } + function setConsent(platform) { + videoConsent[platform] = true; + localStorage.setItem(consentKey, JSON.stringify(videoConsent)); + document.cookie = `${platform}_consent=true; path=/; max-age=${30 * 24 * 60 * 60}; SameSite=Lax; Secure`; + } - function hasConsent(platform) { - return videoConsent[platform] === true || document.cookie.includes(`${platform}_consent=true`); - } + function hasConsent(platform) { + return videoConsent[platform] === true || document.cookie.includes(`${platform}_consent=true`); + } - function loadVideo(placeholder) { - const platform = placeholder.dataset.platform; - const videoId = placeholder.dataset.videoId; - const mediaPlayer = placeholder.nextElementSibling; + function loadVideo(placeholder) { + const { platform, videoId } = placeholder.dataset; + const mediaPlayer = placeholder.nextElementSibling; - if (mediaPlayer && mediaPlayer.tagName.toLowerCase() === 'media-player') { - mediaPlayer.style.display = ''; - mediaPlayer.setAttribute('src', `${platform}/${videoId}`); - placeholder.style.display = 'none'; - } else { - console.error('Media player element not found'); - } + if (mediaPlayer?.tagName.toLowerCase() === 'media-player') { + mediaPlayer.style.display = ''; + mediaPlayer.setAttribute('src', `${platform}/${videoId}`); + placeholder.style.display = 'none'; + } else { + console.error('Media player element not found'); } + } - function updatePlaceholder(placeholder, consented) { - const button = placeholder.querySelector('.consent-button'); - const lang = document.documentElement.lang || 'en'; - - if (consented) { - button.textContent = getText('Consent given', lang); - button.disabled = true; - } else { - button.textContent = getText('Load Video', lang); - button.disabled = false; - } + function updatePlaceholder(placeholder, consented) { + const button = placeholder.querySelector('.consent-button'); + const lang = document.documentElement.lang || 'en'; + + if (button) { + button.textContent = getText(consented ? 'Consent given' : 'Load Video', lang); + button.disabled = consented; } + } - function initializePlaceholders() { - document.querySelectorAll('.consent-placeholder').forEach(placeholder => { - const platform = placeholder.dataset.platform; - const consentButton = placeholder.querySelector('.consent-button'); - const consented = hasConsent(platform); + function initializePlaceholder(placeholder) { + const platform = placeholder.dataset.platform; + const consented = hasConsent(platform); - updatePlaceholder(placeholder, consented); + updatePlaceholder(placeholder, consented); - if (consented) { + if (consented) { + loadVideo(placeholder); + } else { + const consentButton = placeholder.querySelector('.consent-button'); + if (consentButton) { + consentButton.addEventListener('click', () => { + setConsent(platform); + updatePlaceholder(placeholder, true); loadVideo(placeholder); - } else { - if (consentButton) { - consentButton.addEventListener('click', () => { - setConsent(platform); - updatePlaceholder(placeholder, true); - loadVideo(placeholder); - }); - } else { - console.error('Consent button not found in placeholder'); - } - } - }); + }); + } else { + console.error('Consent button not found in placeholder'); + } } + } - function initializeMediaPlayers() { - document.querySelectorAll('media-player[data-video-platform]').forEach(player => { - const platform = player.dataset.videoPlatform; - if (hasConsent(platform)) { - const videoId = player.dataset.videoId; - player.style.display = ''; - player.setAttribute('src', `${platform}/${videoId}`); - const placeholder = player.previousElementSibling; - if (placeholder?.classList.contains('consent-placeholder')) { - placeholder.style.display = 'none'; - } - } - }); + function initializeMediaPlayer(player) { + const platform = player.dataset.videoPlatform; + if (hasConsent(platform)) { + const videoId = player.dataset.videoId; + player.style.display = ''; + player.setAttribute('src', `${platform}/${videoId}`); + const placeholder = player.previousElementSibling; + if (placeholder?.classList.contains('consent-placeholder')) { + placeholder.style.display = 'none'; + } } + } - function applyTranslations() { - const videoLayouts = document.querySelectorAll('media-video-layout'); - videoLayouts.forEach(layout => { + function applyTranslations() { + ['media-video-layout', 'media-audio-layout'].forEach(selector => { + document.querySelectorAll(selector).forEach(layout => { const player = layout.closest('media-player'); const lang = player?.getAttribute('lang') || 'en'; layout.translations = translations[lang] || translations['en']; }); + }); + } - document.querySelectorAll('.consent-placeholder').forEach(placeholder => { - const lang = document.documentElement.lang || 'en'; - const button = placeholder.querySelector('.consent-button'); - const text = placeholder.querySelector('p'); - if (button) { - // button.textContent = getText('Load Video', lang); - } - if (text) { - // text.textContent = getText('Click here to load and play the video.', lang); - } - }); - } - - initializePlaceholders(); - initializeMediaPlayers(); + function initializeElements() { + document.querySelectorAll('.consent-placeholder').forEach(initializePlaceholder); + document.querySelectorAll('media-player[data-video-platform]').forEach(initializeMediaPlayer); applyTranslations(); + } + + async function initializeScript() { + await loadTranslations(); + initializeElements(); - // Beobachter für dynamisch hinzugefügte Elemente const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.classList.contains('consent-placeholder')) { - initializePlaceholders(); + initializePlaceholder(node); } if (node.tagName.toLowerCase() === 'media-player') { - initializeMediaPlayers(); + initializeMediaPlayer(node); applyTranslations(); } } @@ -145,19 +126,15 @@ observer.observe(document.body, { childList: true, subtree: true }); } - // Skript bei DOMContentLoaded ausführen - document.addEventListener('DOMContentLoaded', initializeScript); + ['DOMContentLoaded', 'vsrun'].forEach(event => + document.addEventListener(event, initializeScript) + ); - // Skript bei rex:ready ausführen, wenn jQuery verfügbar ist if (typeof jQuery !== 'undefined') { jQuery(document).on('rex:ready', initializeScript); } - // Skript bei vsrun-Event ausführen - document.addEventListener('vsrun', initializeScript); - - // Skript sofort ausführen, wenn das DOM bereits geladen ist - if (document.readyState === 'complete' || document.readyState === 'interactive') { + if (['complete', 'interactive'].includes(document.readyState)) { initializeScript(); } })(); diff --git a/lib/video.php b/lib/video.php index 05e1255..e93b642 100644 --- a/lib/video.php +++ b/lib/video.php @@ -23,7 +23,7 @@ public function __construct(string $source, string $title = '', string $lang = ' $this->source = $source; $this->title = $title; $this->lang = $lang; - $this->attributes['lang'] = $lang; + $this->attributes['lang'] = $lang; $this->loadTranslations(); } @@ -98,7 +98,7 @@ private function getAlternativeUrl(): string public static function isMedia($url): bool { $mediaExtensions = ['mp4', 'mov', 'm4v', 'ogg', 'webm', 'mp3', 'wav', 'aac', 'm4a']; - + if (filter_var($url, FILTER_VALIDATE_URL)) { $pathInfo = pathinfo(parse_url($url, PHP_URL_PATH)); } else { @@ -108,14 +108,14 @@ public static function isMedia($url): bool } $pathInfo = pathinfo($media->getFileName()); } - + return in_array(strtolower($pathInfo['extension'] ?? ''), $mediaExtensions); } public static function isAudio($url): bool { $audioExtensions = ['mp3', 'ogg', 'wav', 'aac', 'm4a']; - + if (filter_var($url, FILTER_VALIDATE_URL)) { $pathInfo = pathinfo(parse_url($url, PHP_URL_PATH)); } else { @@ -125,7 +125,7 @@ public static function isAudio($url): bool } $pathInfo = pathinfo($media->getFileName()); } - + return in_array(strtolower($pathInfo['extension'] ?? ''), $audioExtensions); } @@ -145,9 +145,6 @@ private function getVideoInfo(): array public function generateFull(): string { $videoInfo = $this->getVideoInfo(); - $attributesString = $this->generateAttributesString(); - $titleAttr = $this->title ? " title=\"" . rex_escape($this->title) . "\"" : ''; - $isAudio = self::isAudio($this->source); $mediaType = $isAudio ? 'audio' : 'video'; @@ -163,28 +160,8 @@ public function generateFull(): string $code .= $this->generateConsentPlaceholder($consentText, $videoInfo['platform'], $videoInfo['id']); } - $code .= "getText('a11y_video_from')) . " " . rex_escape($videoInfo['platform']) . "\""; - } else { - $code .= " src=\"" . rex_escape($this->getSourceUrl()) . "\""; - } - - $code .= " role=\"application\"" . (!$isAudio && $videoInfo['platform'] !== 'default' ? " style=\"display:none;\"" : "") . ">"; - $code .= ""; - - if (!$isAudio) { - foreach ($this->subtitles as $subtitle) { - $defaultAttr = $subtitle['default'] ? ' default' : ''; - $code .= ""; - } - } - - $code .= $isAudio ? "" : - "thumbnails ? " thumbnails=\"" . rex_escape($this->thumbnails) . "\"" : "") . ">"; - $code .= ""; + // Use generate() to create the core media player markup + $code .= $this->generate(); if (!$isAudio && $this->a11yContent) { $code .= "
getText('a11y_additional_information')) . "\">" @@ -203,23 +180,34 @@ public function generate(): string $sourceUrl = $this->getSourceUrl(); $isAudio = self::isAudio($this->source); $mediaType = $isAudio ? 'audio' : 'video'; + $videoInfo = $this->getVideoInfo(); + + $code = "getText('a11y_video_from')) . " " . rex_escape($videoInfo['platform']) . "\""; + } else { + $code .= " src=\"" . rex_escape($sourceUrl) . "\""; + } - $code = "getText("a11y_{$mediaType}_player")) . "\">"; + $code .= " role=\"application\"" . (!$isAudio && $videoInfo['platform'] !== 'default' ? " style=\"display:none;\"" : "") . ">"; $code .= ""; - + if (!$isAudio) { foreach ($this->subtitles as $subtitle) { $defaultAttr = $subtitle['default'] ? ' default' : ''; $code .= ""; } } - - $code .= $isAudio ? "" : + + $code .= $isAudio ? "" : "thumbnails ? " thumbnails=\"" . rex_escape($this->thumbnails) . "\"" : "") . ">"; $code .= ""; return $code; } + private function generateAttributesString(): string { return array_reduce(array_keys($this->attributes), function ($carry, $key) { @@ -258,7 +246,7 @@ public static function parseOembedTags(string $content): string }, $content); } - public static function show_sidebar(\rex_extension_point $ep): ?string + public static function show_sidebar(\rex_extension_point $ep): ?string { $params = $ep->getParams(); $file = $params['filename'];