diff --git a/app/Http/Controllers/Backend/DashboardController.php b/app/Http/Controllers/Backend/DashboardController.php index fb47f259..5954d14b 100644 --- a/app/Http/Controllers/Backend/DashboardController.php +++ b/app/Http/Controllers/Backend/DashboardController.php @@ -84,8 +84,8 @@ public function __invoke(OpencastService $opencastService): Application|Factory| 'userSeries' => auth()->user()->getAllSeries() ->CurrentSemester() ->withLastPublicClip() - ->orderByDesc('updated_at') - ->simplePaginate(12), + ->orderBy('title') + ->simplePaginate(100), 'userClips' => auth()->user()->clips() ->whereNull('series_id') ->orderByDesc('updated_at') diff --git a/app/Http/Controllers/Frontend/HomeController.php b/app/Http/Controllers/Frontend/HomeController.php index 1663b9ca..b6d048b1 100644 --- a/app/Http/Controllers/Frontend/HomeController.php +++ b/app/Http/Controllers/Frontend/HomeController.php @@ -32,7 +32,7 @@ public function __invoke(): View ->public() ->whereHas('assets') ->single() - ->orderByDesc('updated_at') + ->orderByDesc('recording_date') ->limit(12) ->get(), ]); diff --git a/app/Http/Controllers/Frontend/ShowClipsController.php b/app/Http/Controllers/Frontend/ShowClipsController.php index dc30812a..0f972989 100644 --- a/app/Http/Controllers/Frontend/ShowClipsController.php +++ b/app/Http/Controllers/Frontend/ShowClipsController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Models\Clip; use App\Services\WowzaService; +use Debugbar; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\View\View; @@ -45,9 +46,32 @@ public function show(Clip $clip, WowzaService $wowzaService): View return $value !== 'PDF/CC'; }); + $wowzaStatus = $wowzaService->getHealth(); + $urls = collect([]); + $defaultPlayerUrl = ''; + if ($wowzaStatus) { + $urls = $wowzaService->vodSecureUrls($clip); + + if (empty($urls)) { + $defaultPlayerUrl = []; + } elseif ($urls->has('composite')) { + $defaultPlayerUrl = $urls['composite']; + } elseif ($urls->has('presenter')) { + $defaultPlayerUrl = $urls['presenter']; + } elseif ($urls->has('presentation')) { + $defaultPlayerUrl = $urls['presentation']; + } else { + $defaultPlayerUrl = []; + } + } + + Debugbar::info($defaultPlayerUrl); + return view('frontend.clips.show', [ 'clip' => $clip, 'wowzaStatus' => $wowzaService->getHealth(), + 'defaultVideoUrl' => $defaultPlayerUrl, + 'alternativeVideoUrls' => $urls, 'previousNextClipCollection' => $clip->previousNextClipCollection(), 'assetsResolutions' => $assetsResolutions, ]); diff --git a/app/Policies/ClipPolicy.php b/app/Policies/ClipPolicy.php index ff004446..6d2e0b8d 100644 --- a/app/Policies/ClipPolicy.php +++ b/app/Policies/ClipPolicy.php @@ -44,6 +44,7 @@ public function view(?User $user, Clip $clip): bool } elseif ($clip->is_public && (is_null($clip->series->is_public) || $clip->series->is_public) && $clip->assets()->count() > 0) { + return true; } else { return false; diff --git a/app/Services/OpenSearchService.php b/app/Services/OpenSearchService.php index 06f5a765..72af3bf1 100644 --- a/app/Services/OpenSearchService.php +++ b/app/Services/OpenSearchService.php @@ -166,7 +166,7 @@ public function searchIndexes( } $isPhrase = Str::contains($searchTerm, ' '); - Debugbar::info($isPhrase); + $query = ($isPhrase) ? [ // 'multi_match' => [ diff --git a/app/Services/WowzaService.php b/app/Services/WowzaService.php index 6233db6b..b7ed0c43 100644 --- a/app/Services/WowzaService.php +++ b/app/Services/WowzaService.php @@ -193,27 +193,43 @@ public function findWowzaAssetBitrate($videoPixelHeight): int /** * Generates a wowza streaming link with secure token settings */ - public function vodSecureUrl(Clip $clip): bool|string + public function vodSecureUrls(Clip $clip): Collection { + $filePaths = collect(); if ($clip->assets->count() > 0) { - $contentUrl = getClipSmilFile($clip, config('wowza.check_fautv_links')); - - $contentPath = (config('wowza.check_fautv_links')) - ? $this->streamingSettings->data['content_path'].getClipStoragePath($clip).'camera.smil' - : $this->streamingSettings->data['content_path'].getClipStoragePath($clip).'presenter.smil'; - $secureToken = $this->streamingSettings->data['secure_token']; - $tokenPrefix = $this->streamingSettings->data['token_prefix']; - $tokenStartTime = $tokenPrefix.'starttime='.time(); - $tokenEndTime = $tokenPrefix.'endTime='.(time() + 21600); - - $userIP = (App::environment(['testing', 'local'])) ? env('FAUTV_USER_IP') : $_SERVER['REMOTE_ADDR']; - $hashStr = "{$contentPath}?{$userIP}&{$secureToken}&{$tokenEndTime}&{$tokenStartTime}"; - $hash = hash('sha256', $hashStr, 1); - $usableHash = strtr(base64_encode($hash), '+/', '-_'); - - return "{$contentUrl}?{$tokenStartTime}&{$tokenEndTime}&{$tokenPrefix}hash={$usableHash}"; - } else { - return false; + $clip->getAssetsByType(Content::SMIL)->each(function ($asset) use ($filePaths) { + $fileType = Str::before($asset->original_file_name, '.smil'); + if (config('wowza.check_fautv_links')) { + if (Str::contains($asset->original_file_name, 'composite')) { + $asset->original_file_name = 'combined.smil'; + } elseif (Str::contains($asset->original_file_name, 'presenter')) { + $asset->original_file_name = 'camera.smil'; + } else { + $asset->original_file_name = 'slides.smil'; + } + } + + $url = + config('wowza.stream_url').config('wowza.content_path'). + $asset->path.$asset->original_file_name.'/playlist.m3u8'; + $wowzaContentPath = + $this->streamingSettings->data['content_path']. + $asset->path. + $asset->original_file_name; + $secureToken = $this->streamingSettings->data['secure_token']; + $tokenPrefix = $this->streamingSettings->data['token_prefix']; + $tokenStartTime = $tokenPrefix.'starttime='.time(); + $tokenEndTime = $tokenPrefix.'endTime='.(time() + 21600); + + $userIP = (App::environment(['testing', 'local'])) ? env('FAUTV_USER_IP') : $_SERVER['REMOTE_ADDR']; + $hashStr = "{$wowzaContentPath}?{$userIP}&{$secureToken}&{$tokenEndTime}&{$tokenStartTime}"; + $hash = hash('sha256', $hashStr, 1); + $usableHash = strtr(base64_encode($hash), '+/', '-_'); + $urlWithToken = "{$url}?{$tokenStartTime}&{$tokenEndTime}&{$tokenPrefix}hash={$usableHash}"; + $filePaths->put($fileType, $urlWithToken); + }); } + + return $filePaths; } } diff --git a/app/View/Components/Player.php b/app/View/Components/Player.php index de0a21a7..74c9ffdb 100644 --- a/app/View/Components/Player.php +++ b/app/View/Components/Player.php @@ -4,6 +4,7 @@ use App\Models\Clip; use App\Services\WowzaService; +use Debugbar; use Illuminate\Support\Collection; use Illuminate\View\Component; use Illuminate\View\View; @@ -15,8 +16,12 @@ class Player extends Component * * @return void */ - public function __construct(public Clip $clip, public Collection $wowzaStatus, public WowzaService $wowzaService) - { + public function __construct( + public Clip $clip, + public Collection $wowzaStatus, + // public WowzaService $wowzaService, + public $defaultVideoUrl + ) { // } @@ -25,6 +30,22 @@ public function __construct(public Clip $clip, public Collection $wowzaStatus, p */ public function render(): View { + // $urls = $this->wowzaService->vodSecureUrls($this->clip); + // + // if (empty($urls)) { + // $defaultPlayerUrl = []; + // } elseif ($urls->has('composite')) { + // $defaultPlayerUrl = $urls['composite']; + // } elseif ($urls->has('presenter')) { + // $defaultPlayerUrl = $urls['presenter']; + // } elseif ($urls->has('presentation')) { + // $defaultPlayerUrl = $urls['presentation']; + // } else { + // $defaultPlayerUrl = []; + // } + // + // Debugbar::info($urls); + return view('components.player'); } } diff --git a/app/helpers.php b/app/helpers.php index 1e543c4f..08f60858 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -1,7 +1,6 @@ folder_id}/"; } -function getClipSmilFile(Clip $clip, bool $checkFAUTVLinks = true): string -{ - if ($checkFAUTVLinks) { - return - config('wowza.stream_url'). - config('wowza.content_path'). - getClipStoragePath($clip). - 'camera.smil/playlist.m3u8'; - } else { - return - config('wowza.stream_url'). - config('wowza.content_path'). - getClipStoragePath($clip). - $clip->getAssetsByType(Content::SMIL)->first()?->original_file_name. - '/playlist.m3u8'; - } -} - /* * Fetch all files in the dropzone with sha1 hash * @param bool $ffmpegCheck diff --git a/composer.lock b/composer.lock index 95453e3b..0cd2d7bb 100644 --- a/composer.lock +++ b/composer.lock @@ -633,16 +633,16 @@ }, { "name": "doctrine/inflector", - "version": "2.0.8", + "version": "2.0.9", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" + "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/2930cd5ef353871c821d5c43ed030d39ac8cfe65", + "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65", "shasum": "" }, "require": { @@ -704,7 +704,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.8" + "source": "https://github.com/doctrine/inflector/tree/2.0.9" }, "funding": [ { @@ -720,7 +720,7 @@ "type": "tidelift" } ], - "time": "2023-06-16T13:40:37+00:00" + "time": "2024-01-15T18:05:13+00:00" }, { "name": "doctrine/lexer", @@ -1794,16 +1794,16 @@ }, { "name": "laravel/framework", - "version": "v10.40.0", + "version": "v10.41.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "7a9470071dac9579ebf29ad1b9d73e4b8eb586fc" + "reference": "da31969bd35e6ee0bbcd9e876f88952dc754b012" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/7a9470071dac9579ebf29ad1b9d73e4b8eb586fc", - "reference": "7a9470071dac9579ebf29ad1b9d73e4b8eb586fc", + "url": "https://api.github.com/repos/laravel/framework/zipball/da31969bd35e6ee0bbcd9e876f88952dc754b012", + "reference": "da31969bd35e6ee0bbcd9e876f88952dc754b012", "shasum": "" }, "require": { @@ -1995,20 +1995,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-01-09T11:46:47+00:00" + "time": "2024-01-16T15:23:58+00:00" }, { "name": "laravel/pint", - "version": "v1.13.8", + "version": "v1.13.9", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "69def89df9e0babc0f0a8bea184804a7d8a9c5c0" + "reference": "e3e269cc5d874c8efd2dc7962b1c7ff2585fe525" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/69def89df9e0babc0f0a8bea184804a7d8a9c5c0", - "reference": "69def89df9e0babc0f0a8bea184804a7d8a9c5c0", + "url": "https://api.github.com/repos/laravel/pint/zipball/e3e269cc5d874c8efd2dc7962b1c7ff2585fe525", + "reference": "e3e269cc5d874c8efd2dc7962b1c7ff2585fe525", "shasum": "" }, "require": { @@ -2019,13 +2019,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.46.0", - "illuminate/view": "^10.39.0", + "friendsofphp/php-cs-fixer": "^3.47.0", + "illuminate/view": "^10.40.0", "larastan/larastan": "^2.8.1", "laravel-zero/framework": "^10.3.0", "mockery/mockery": "^1.6.7", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.30.0" + "pestphp/pest": "^2.31.0" }, "bin": [ "builds/pint" @@ -2061,7 +2061,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-01-09T18:03:54+00:00" + "time": "2024-01-16T17:39:29+00:00" }, { "name": "laravel/prompts", @@ -3043,16 +3043,16 @@ }, { "name": "nette/utils", - "version": "v4.0.3", + "version": "v4.0.4", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", + "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", "shasum": "" }, "require": { @@ -3123,9 +3123,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.3" + "source": "https://github.com/nette/utils/tree/v4.0.4" }, - "time": "2023-10-29T21:02:13+00:00" + "time": "2024-01-17T16:50:36+00:00" }, { "name": "nikic/php-parser", @@ -8898,16 +8898,16 @@ }, { "name": "laravel/breeze", - "version": "v1.28.0", + "version": "v1.28.1", "source": { "type": "git", "url": "https://github.com/laravel/breeze.git", - "reference": "6856cd4725b0f261b2d383b01a3875744051acf5" + "reference": "e853918e770822780efd160a73fd676992340aca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/breeze/zipball/6856cd4725b0f261b2d383b01a3875744051acf5", - "reference": "6856cd4725b0f261b2d383b01a3875744051acf5", + "url": "https://api.github.com/repos/laravel/breeze/zipball/e853918e770822780efd160a73fd676992340aca", + "reference": "e853918e770822780efd160a73fd676992340aca", "shasum": "" }, "require": { @@ -8956,32 +8956,32 @@ "issues": "https://github.com/laravel/breeze/issues", "source": "https://github.com/laravel/breeze" }, - "time": "2024-01-06T17:23:00+00:00" + "time": "2024-01-15T16:14:10+00:00" }, { "name": "laravel/envoy", - "version": "v2.8.7", + "version": "v2.9.0", "source": { "type": "git", "url": "https://github.com/laravel/envoy.git", - "reference": "dd7635009d8d22a34a9f9c2a4e282d23ee234a07" + "reference": "a9b9da0fb5eb6625d90a164b828e94ab81472eb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/envoy/zipball/dd7635009d8d22a34a9f9c2a4e282d23ee234a07", - "reference": "dd7635009d8d22a34a9f9c2a4e282d23ee234a07", + "url": "https://api.github.com/repos/laravel/envoy/zipball/a9b9da0fb5eb6625d90a164b828e94ab81472eb2", + "reference": "a9b9da0fb5eb6625d90a164b828e94ab81472eb2", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.0|^7.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", "php": "^7.2|^8.0", - "symfony/console": "^4.3|^5.0|^6.0", - "symfony/process": "^4.3|^5.0|^6.0" + "symfony/console": "^4.3|^5.0|^6.0|^7.0", + "symfony/process": "^4.3|^5.0|^6.0|^7.0" }, "require-dev": { "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.0|^9.0" + "phpunit/phpunit": "^8.0|^9.0|^10.4" }, "suggest": { "ext-posix": "Required to determine the System user on Unix systems." @@ -9017,22 +9017,22 @@ ], "support": { "issues": "https://github.com/laravel/envoy/issues", - "source": "https://github.com/laravel/envoy/tree/v2.8.7" + "source": "https://github.com/laravel/envoy/tree/v2.9.0" }, - "time": "2024-01-09T17:59:31+00:00" + "time": "2024-01-16T17:18:44+00:00" }, { "name": "laravel/sail", - "version": "v1.27.0", + "version": "v1.27.1", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "65a7764af5daadbd122e3b0d67be371d158a9b9a" + "reference": "9dc648978e4276f2bfd37a076a52e3bd9394777f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/65a7764af5daadbd122e3b0d67be371d158a9b9a", - "reference": "65a7764af5daadbd122e3b0d67be371d158a9b9a", + "url": "https://api.github.com/repos/laravel/sail/zipball/9dc648978e4276f2bfd37a076a52e3bd9394777f", + "reference": "9dc648978e4276f2bfd37a076a52e3bd9394777f", "shasum": "" }, "require": { @@ -9084,7 +9084,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-01-03T14:07:34+00:00" + "time": "2024-01-13T18:46:48+00:00" }, { "name": "maximebf/debugbar", @@ -10324,16 +10324,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.55", + "version": "1.10.56", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "9a88f9d18ddf4cf54c922fbeac16c4cb164c5949" + "reference": "27816a01aea996191ee14d010f325434c0ee76fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9a88f9d18ddf4cf54c922fbeac16c4cb164c5949", - "reference": "9a88f9d18ddf4cf54c922fbeac16c4cb164c5949", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/27816a01aea996191ee14d010f325434c0ee76fa", + "reference": "27816a01aea996191ee14d010f325434c0ee76fa", "shasum": "" }, "require": { @@ -10382,7 +10382,7 @@ "type": "tidelift" } ], - "time": "2024-01-08T12:32:40+00:00" + "time": "2024-01-15T10:43:00+00:00" }, { "name": "phpunit/php-code-coverage", @@ -11938,16 +11938,16 @@ }, { "name": "spatie/laravel-ignition", - "version": "2.4.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "b9395ba48d3f30d42092cf6ceff75ed7256cd604" + "reference": "005e1e7b1232f3b22d7e7be3f602693efc7dba67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/b9395ba48d3f30d42092cf6ceff75ed7256cd604", - "reference": "b9395ba48d3f30d42092cf6ceff75ed7256cd604", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/005e1e7b1232f3b22d7e7be3f602693efc7dba67", + "reference": "005e1e7b1232f3b22d7e7be3f602693efc7dba67", "shasum": "" }, "require": { @@ -12026,7 +12026,7 @@ "type": "github" } ], - "time": "2024-01-04T14:51:24+00:00" + "time": "2024-01-12T13:14:58+00:00" }, { "name": "symfony/yaml", diff --git a/package-lock.json b/package-lock.json index e3767178..6d36476a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -558,9 +558,9 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -701,9 +701,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.1.tgz", - "integrity": "sha512-18PLWRzhy9glDQp3+wOgfLYRWlhgX0azxgJ63rdpoUHyrC9z0f5CkFburjQx4uD7ZCruw85ZtMt6K+L+R8fLJQ==", + "version": "8.56.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", + "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", "dev": true, "peer": true, "dependencies": { @@ -752,9 +752,9 @@ } }, "node_modules/@types/node": { - "version": "20.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.7.tgz", - "integrity": "sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==", + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", "devOptional": true, "dependencies": { "undici-types": "~5.26.4" @@ -1400,9 +1400,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.16", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", - "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", "dev": true, "funding": [ { @@ -1419,9 +1419,9 @@ } ], "dependencies": { - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001538", - "fraction.js": "^4.3.6", + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -2242,9 +2242,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001576", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", - "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "dev": true, "funding": [ { @@ -2885,19 +2885,19 @@ } }, "node_modules/css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.9.0.tgz", + "integrity": "sha512-3I5Nu4ytWlHvOP6zItjiHlefBNtrH+oehq8tnQa2kO305qpVyx9XNIT1CXIj5bgCJs7qICBCkgCYxQLKPANoLA==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.21", + "postcss": "^8.4.31", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", + "postcss-modules-scope": "^3.1.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">= 12.13.0" @@ -3281,9 +3281,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.625", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz", - "integrity": "sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q==", + "version": "1.4.637", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.637.tgz", + "integrity": "sha512-G7j3UCOukFtxVO1vWrPQUoDk3kL70mtvjc/DC/k2o7lE0wAdq+Vwp1ipagOow+BH0uVztFysLWbkM/RTIrbK3w==", "dev": true }, "node_modules/elliptic": { @@ -4016,9 +4016,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true, "funding": [ { @@ -6783,9 +6783,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz", + "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==", "dev": true, "dependencies": { "icss-utils": "^5.0.0", @@ -8589,14 +8589,15 @@ "dev": true }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", "dependencies": { "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -8827,9 +8828,9 @@ } }, "node_modules/socket.io": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.3.tgz", - "integrity": "sha512-SE+UIQXBQE+GPG2oszWMlsEmWtHVqw/h1VrYJGK5/MC7CH5p58N448HwIrtREcvR4jfdOJAY4ieQfxMr55qbbw==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz", + "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==", "dev": true, "dependencies": { "accepts": "~1.3.4", @@ -8854,9 +8855,9 @@ } }, "node_modules/socket.io-client": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.3.tgz", - "integrity": "sha512-nU+ywttCyBitXIl9Xe0RSEfek4LneYkJxCeNnKCuhwoH4jGXO1ipIUw/VA/+Vvv2G1MTym11fzFC0SxkrcfXDw==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz", + "integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==", "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -9448,9 +9449,9 @@ } }, "node_modules/terser": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", - "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", "devOptional": true, "peer": true, "dependencies": { diff --git a/resources/js/app.js b/resources/js/app.js index b585ade9..de48df5d 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,16 +1,18 @@ -import "boot"; +import 'boot'; import { - Livewire, - Alpine, -} from "../../vendor/livewire/livewire/dist/livewire.esm"; -import jQuery from "jquery"; -import Hls from "hls.js"; -import Pikaday from "pikaday"; -import * as FilePond from "filepond"; -import FilePondPluginImagePreview from "filepond-plugin-image-preview"; -import "filepond/dist/filepond.min.css"; -import select2 from "select2"; + Livewire, + Alpine, +} from '../../vendor/livewire/livewire/dist/livewire.esm'; +import jQuery from 'jquery'; +import Hls from 'hls.js'; +import Pikaday from 'pikaday'; +import * as FilePond from 'filepond'; +import FilePondPluginImagePreview from 'filepond-plugin-image-preview'; +import 'filepond/dist/filepond.min.css'; +import select2 from 'select2'; +import Plyr from 'plyr'; +import 'plyr/dist/plyr.css'; Livewire.start(); @@ -18,281 +20,328 @@ window.jQuery = window.$ = jQuery; window.Pikaday = Pikaday; window.select2 = select2(); -$(".solution-trix-field-wrapper") - .find($("trix-editor")) - .css("min-height", "350px"); +$('.solution-trix-field-wrapper') + .find($('trix-editor')) + .css('min-height', '350px'); -document.addEventListener("alpine:init", () => { - Alpine.store("darkMode", { - on: Alpine.$persist(false), - }); +document.addEventListener('alpine:init', () => { + Alpine.store('darkMode', { + on: Alpine.$persist(false), + }); }); document.addEventListener( - "DOMContentLoaded", - function () { - $(".select2-tides").select2(); + 'DOMContentLoaded', + function () { + $('.select2-tides').select2(); - $(".select2-tides-clips").select2({ - allowClear: true, - placeholder: "Search for a clip", - tags: true, - minimumInputLength: 2, - ajax: { - url: "/api/clips/", - delay: 250, - data: function (params) { - return { - query: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - params.page = params.page || 1; - return { - results: $.map(data, function (obj) { - return { id: obj.id, text: obj.name }; - }), - pagination: { - more: params.page * 30 < data.total_count, - }, - }; - }, + $('.select2-tides-clips').select2({ + allowClear: true, + placeholder: 'Search for a clip', + tags: true, + minimumInputLength: 2, + ajax: { + url: '/api/clips/', + delay: 250, + data: function (params) { + return { + query: params.term, // search term + page: params.page, + }; + }, + processResults: function (data, params) { + params.page = params.page || 1; + return { + results: $.map(data, function (obj) { + return { id: obj.id, text: obj.name }; + }), + pagination: { + more: params.page * 30 < data.total_count, }, - }); + }; + }, + }, + }); - $(".select2-tides-tags").select2({ - allowClear: true, - placeholder: "Add a tag", - tags: true, - minimumInputLength: 2, - ajax: { - url: "/api/tags/", - delay: 250, - data: function (params) { - return { - query: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - params.page = params.page || 1; - return { - results: $.map(data, function (obj) { - return { id: obj.name, text: obj.name }; - }), - pagination: { - more: params.page * 30 < data.total_count, - }, - }; - }, + $('.select2-tides-tags').select2({ + allowClear: true, + placeholder: 'Add a tag', + tags: true, + minimumInputLength: 2, + ajax: { + url: '/api/tags/', + delay: 250, + data: function (params) { + return { + query: params.term, // search term + page: params.page, + }; + }, + processResults: function (data, params) { + params.page = params.page || 1; + return { + results: $.map(data, function (obj) { + return { id: obj.name, text: obj.name }; + }), + pagination: { + more: params.page * 30 < data.total_count, }, - }); + }; + }, + }, + }); - $(".select2-tides-presenters").select2({ - allowClear: true, - placeholder: "Add a presenter", - minimumInputLength: 2, - ajax: { - url: "/api/presenters/", - delay: 250, - data: function (params) { - return { - query: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - params.page = params.page || 1; - return { - results: $.map(data, function (obj) { - return { id: obj.id, text: obj.name }; - }), - pagination: { - more: params.page * 30 < data.total_count, - }, - }; - }, + $('.select2-tides-presenters').select2({ + allowClear: true, + placeholder: 'Add a presenter', + minimumInputLength: 2, + ajax: { + url: '/api/presenters/', + delay: 250, + data: function (params) { + return { + query: params.term, // search term + page: params.page, + }; + }, + processResults: function (data, params) { + params.page = params.page || 1; + return { + results: $.map(data, function (obj) { + return { id: obj.id, text: obj.name }; + }), + pagination: { + more: params.page * 30 < data.total_count, }, - }); + }; + }, + }, + }); - $(".select2-tides-users").select2({ - placeholder: "Search for a user", - minimumInputLength: 2, - ajax: { - url: "/api/users/", - delay: 250, - data: function (params) { - return { - query: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - params.page = params.page || 1; - return { - results: $.map(data, function (obj) { - return { id: obj.id, text: obj.name }; - }), - pagination: { - more: params.page * 30 < data.total_count, - }, - }; - }, + $('.select2-tides-users').select2({ + placeholder: 'Search for a user', + minimumInputLength: 2, + ajax: { + url: '/api/users/', + delay: 250, + data: function (params) { + return { + query: params.term, // search term + page: params.page, + }; + }, + processResults: function (data, params) { + params.page = params.page || 1; + return { + results: $.map(data, function (obj) { + return { id: obj.id, text: obj.name }; + }), + pagination: { + more: params.page * 30 < data.total_count, }, - }); + }; + }, + }, + }); - $(".select2-tides-organization").select2({ - placeholder: "select an organization", - minimumInputLength: 2, - ajax: { - url: "/api/organizations/", - delay: 250, - data: function (params) { - return { - query: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - params.page = params.page || 1; - return { - results: $.map(data, function (obj) { - return { id: obj.id, text: obj.name }; - }), - pagination: { - more: params.page * 30 < data.total_count, - }, - }; - }, + $('.select2-tides-organization').select2({ + placeholder: 'select an organization', + minimumInputLength: 2, + ajax: { + url: '/api/organizations/', + delay: 250, + data: function (params) { + return { + query: params.term, // search term + page: params.page, + }; + }, + processResults: function (data, params) { + params.page = params.page || 1; + return { + results: $.map(data, function (obj) { + return { id: obj.id, text: obj.name }; + }), + pagination: { + more: params.page * 30 < data.total_count, }, - }); + }; + }, + }, + }); - $(".select2-tides-images").select2({ - placeholder: "Select an image", - minimumInputLength: 2, - ajax: { - url: "/api/images/", - delay: 250, - dataType: "json", - data: function (params) { - return { - query: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - params.page = params.page || 1; - return { - results: $.map(data, function (obj) { - return { id: obj.id, text: obj.name }; - }), - pagination: { - more: params.page * 30 < data.total_count, - }, - }; - }, - }, - templateResult: format, - templateSelection: formatSelect, - escapeMarkup: function (m) { - return m; + $('.select2-tides-images').select2({ + placeholder: 'Select an image', + minimumInputLength: 2, + ajax: { + url: '/api/images/', + delay: 250, + dataType: 'json', + data: function (params) { + return { + query: params.term, // search term + page: params.page, + }; + }, + processResults: function (data, params) { + params.page = params.page || 1; + return { + results: $.map(data, function (obj) { + return { id: obj.id, text: obj.name }; + }), + pagination: { + more: params.page * 30 < data.total_count, }, - }); + }; + }, + }, + templateResult: format, + templateSelection: formatSelect, + escapeMarkup: function (m) { + return m; + }, + }); - function format(state) { - if (!state.id) return state.text; // optgroup - return ( - '
' + - '' + - "
" + - "
" + - state.text.slice(0, 15) + - "
" + - "
" - ); - } + function format(state) { + if (!state.id) return state.text; // optgroup + return ( + '
' + + '' + + '
' + + '
' + + state.text.slice(0, 15) + + '
' + + '
' + ); + } - function formatSelect(state) { - if (!state.id) return state.text; - return state.text; - } + function formatSelect(state) { + if (!state.id) return state.text; + return state.text; + } - const video = document.querySelector("video"); + const video = document.querySelector('video'); - if (video === null) { - console.log("Video element not found"); - } else { - const source = video.getElementsByTagName("source")[0].src; + if (video === null) { + console.log('Video element not found'); + } else { + const source = video.getElementsByTagName('source')[0].src; - // For more options see: https://github.com/sampotts/plyr/#options - // captions.update is required for captions to work with hls.js - const defaultOptions = {}; + // For more options see: https://github.com/sampotts/plyr/#options + // captions.update is required for captions to work with hls.js + const defaultOptions = {}; - if (Hls.isSupported()) { - // For more Hls.js options, see https://github.com/dailymotion/hls.js - const hls = new Hls(); - hls.loadSource(source); + if (Hls.isSupported()) { + // For more Hls.js options, see https://github.com/dailymotion/hls.js + const hls = new Hls(); + hls.loadSource(source); - // From the m3u8 playlist, hls parses the manifest and returns - // all available video qualities. This is important, in this approach, - // we will have one source on the Plyr player. - hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) { - // Transform available levels into an array of integers (height values). - const availableQualities = hls.levels.map((l) => l.height); - defaultOptions.language = "de"; - defaultOptions.iconUrl = "/css/plyr.svg"; - defaultOptions.loadSripte = false; + // From the m3u8 playlist, hls parses the manifest and returns + // all available video qualities. This is important, in this approach, + // we will have one source on the Plyr player. + hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) { + // Transform available levels into an array of integers (height values). + const availableQualities = hls.levels.map((l) => l.height); + defaultOptions.language = 'de'; + defaultOptions.iconUrl = '/css/plyr.svg'; + defaultOptions.loadSripte = false; - // Add new qualities to option - defaultOptions.quality = { - default: availableQualities[0], - options: availableQualities, - // this ensures Plyr to use Hls to update quality level - forced: true, - onChange: (e) => updateQuality(e), - }; + // Add new qualities to option + defaultOptions.quality = { + default: availableQualities[0], + options: availableQualities, + // this ensures Plyr to use Hls to update quality level + forced: true, + onChange: (e) => updateQuality(e), + }; - // Initialize here - const player = new Plyr(video, defaultOptions); - }); - hls.attachMedia(video); - window.hls = hls; - } else { - // default options with no quality update in case Hls is not supported - const player = new Plyr(video, { - language: "de", - iconUrl: "/css/plyr.svg", - loadSprite: false, - }); - } + // Initialize here + const player = new Plyr(video, defaultOptions); + }); + hls.attachMedia(video); + window.hls = hls; + } else { + // default options with no quality update in case Hls is not supported + const player = new Plyr(video, { + language: 'de', + iconUrl: '/css/plyr.svg', + loadSprite: false, + }); + } - function updateQuality(newQuality) { - window.hls.levels.forEach((level, levelIndex) => { - if (level.height === newQuality) { - console.log("Found quality match with " + newQuality); - window.hls.currentLevel = levelIndex; - } - }); - } - } - }, - false + function updateQuality(newQuality) { + window.hls.levels.forEach((level, levelIndex) => { + if (level.height === newQuality) { + console.log('Found quality match with ' + newQuality); + window.hls.currentLevel = levelIndex; + } + }); + } + } + }, + false ); FilePond.registerPlugin(FilePondPluginImagePreview); const inputElement = document.querySelector('input[type="file"].filepond'); const csrfToken = document - .querySelector('meta[name="csrf-token"]') - .getAttribute("content"); + .querySelector('meta[name="csrf-token"]') + .getAttribute('content'); FilePond.create(inputElement).setOptions({ - server: { - process: "/admin/uploads/process", - headers: { - "X-CSRF-TOKEN": csrfToken, - }, + server: { + process: '/admin/uploads/process', + headers: { + 'X-CSRF-TOKEN': csrfToken, }, + }, +}); + +document.addEventListener('DOMContentLoaded', () => { + const player = new Plyr('#video'); + + // Function to change video source + function changeVideoSource(newSource) { + const currentTime = player.currentTime; // Get the current time of the video + + function setNewVideoTime() { + if (player.media.readyState >= 2) { + // Ensure media is ready + player.currentTime = currentTime; // Set the current time for the new video + player.play(); + } else { + setTimeout(setNewVideoTime, 100); // Check again after a short delay + } + } + + if (Hls.isSupported()) { + var hls = new Hls(); + hls.loadSource(newSource); + hls.attachMedia(player.media); + hls.on(Hls.Events.MANIFEST_PARSED, function () { + setNewVideoTime(); // Set the time after the manifest is parsed + }); + } else if (player.media.canPlayType('application/vnd.apple.mpegurl')) { + player.media.src = newSource; + player.media.addEventListener('loadedmetadata', setNewVideoTime); + } else { + player.source = { + type: 'video', + sources: [{ src: newSource, type: 'video/mp4' }], + }; + player.on('loadeddata', setNewVideoTime); // Set the time for standard video formats + } + } + + // Attach event listeners to links + const videoLinks = document.querySelectorAll('.video-link'); + videoLinks.forEach((link) => { + link.addEventListener('click', function (event) { + event.preventDefault(); + const videoUrl = this.getAttribute('href'); + changeVideoSource(videoUrl); + }); + }); }); diff --git a/resources/views/backend/dashboard/index.blade.php b/resources/views/backend/dashboard/index.blade.php index 35fb2389..5518590a 100644 --- a/resources/views/backend/dashboard/index.blade.php +++ b/resources/views/backend/dashboard/index.blade.php @@ -32,16 +32,21 @@ @if($opencastEvents->isNotEmpty()) @include('backend.dashboard._opencast-workflows',['opencastEvents' => $opencastEvents]) @endif - - @include('backend.users.series._layout',['layoutHeader' => __('dashboard.your last series'), 'series'=> $userSeries]) - - @include('backend.users.clips._layout',['layoutHeader' => __('dashboard.your last clips'), 'clips'=> $userClips]) - @if(count($files) > 0 )
@include('backend.dashboard._dropzone-files')
@endif +
+
+ @include('backend.users.series._layout',[ + 'layoutHeader' => __('dashboard.your last series'), + 'series'=> $userSeries]) + @include('backend.users.clips._layout',[ + 'layoutHeader' => __('dashboard.your last clips'), + 'clips'=> $userClips]) +
+
@endsection diff --git a/resources/views/backend/users/series/_layout.blade.php b/resources/views/backend/users/series/_layout.blade.php index d38addcc..4233807b 100644 --- a/resources/views/backend/users/series/_layout.blade.php +++ b/resources/views/backend/users/series/_layout.blade.php @@ -1,7 +1,7 @@
{{ $layoutHeader }}
-
+
@forelse($series as $single) @include('backend.series._card',['series'=> $single]) @empty diff --git a/resources/views/components/list-clips.blade.php b/resources/views/components/list-clips.blade.php index 74a3db95..9f66aa88 100644 --- a/resources/views/components/list-clips.blade.php +++ b/resources/views/components/list-clips.blade.php @@ -218,7 +218,7 @@ class="focus:outline-none text-white text-sm py-1.5 px-5 rounded-md bg-blue-700 @forelse($clips as $clip)
  • + p-2 text-center text-lg font-normal dark:text-white">
    @if ($reorder)
    diff --git a/resources/views/frontend/clips/_player.blade.php b/resources/views/frontend/clips/_player.blade.php index 176b01c5..dfdf3131 100644 --- a/resources/views/frontend/clips/_player.blade.php +++ b/resources/views/frontend/clips/_player.blade.php @@ -2,14 +2,56 @@
    -
    - @if($clip->checkAcls()) - - @else -

    {{ __('clip.frontend.not authorized to view video') }}

    - @endif +
    +
    + @if($clip->checkAcls()) + + @else +

    {{ __('clip.frontend.not authorized to view video') }}

    + @endif +
    +
    +
    + @foreach($alternativeVideoUrls as $type=> $url) +
    + @if($type === 'presenter') + + + + @endif + @if($type === 'presentation') + + + + @endif + @if($type === 'composite') + + + + @endif +
    + @endforeach +
    + +
    +
    diff --git a/tests/Feature/Console/Commands/InsertSmilAssetsTest.php b/tests/Feature/Console/Commands/InsertSmilAssetsTest.php index f23ade84..627b73ed 100644 --- a/tests/Feature/Console/Commands/InsertSmilAssetsTest.php +++ b/tests/Feature/Console/Commands/InsertSmilAssetsTest.php @@ -4,8 +4,6 @@ use Facades\Tests\Setup\ClipFactory; use Illuminate\Support\Facades\Storage; -uses(\Illuminate\Foundation\Testing\RefreshDatabase::class); - it('shows a message if smil is created', function () { $clip = ClipFactory::withAssets(4)->create(); @@ -17,13 +15,11 @@ $clip = ClipFactory::withAssets(4)->create(); - expect($clip->assets()->count())->toEqual(4); + expect($clip->assets()->count())->toEqual(5); $this->artisan('smil:insert'); $smil = $clip->getAssetsByType(Content::SMIL)->first(); $this->assertDatabaseHas('assets', ['id' => $smil->id]); - - Storage::disk('videos')->assertExists($smil->path.$smil->original_file_name); }); diff --git a/tests/Feature/Http/Controllers/Backend/AssetsDestroyTest.php b/tests/Feature/Http/Controllers/Backend/AssetsDestroyTest.php index 2f22858a..a2abc35f 100644 --- a/tests/Feature/Http/Controllers/Backend/AssetsDestroyTest.php +++ b/tests/Feature/Http/Controllers/Backend/AssetsDestroyTest.php @@ -25,10 +25,10 @@ test('a moderator can delete an owned clip asset', function () { $clip = ClipFactory::withAssets(1)->ownedBy(signInRole(Role::MODERATOR))->create(); - expect($clip->assets->count())->toBe(1); + expect($clip->assets->count())->toBe(2); // it will create also the smil file for the video delete(route('assets.destroy', $clip->assets()->first()))->assertRedirect(route('clips.edit', $clip)); - expect($clip->assets()->count())->toBe(0); + expect($clip->assets()->count())->toBe(1); }); test('deleting an asset should also delete the file from storage', function () { diff --git a/tests/Feature/Http/Controllers/Frontend/AccessTest.php b/tests/Feature/Http/Controllers/Frontend/AccessTest.php index 9b100364..66196ceb 100644 --- a/tests/Feature/Http/Controllers/Frontend/AccessTest.php +++ b/tests/Feature/Http/Controllers/Frontend/AccessTest.php @@ -14,16 +14,16 @@ it('a clip with a portal acl can be only be accessable for logged in users', function () { $this->clip->addAcls(collect([Acl::PORTAL()])); - get(route('frontend.clips.show', $this->clip))->assertDontSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertDontSee('player-container'); signIn(); - get(route('frontend.clips.show', $this->clip))->assertSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertSee('player-container'); }); it('a clip with lms acl can be only accessable for lms users', function () { $this->clip->addAcls(collect([Acl::LMS()])); $client = getUrlClientType(Acl::LMS->lower()); - get(route('frontend.clips.show', $this->clip))->assertDontSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertDontSee('player-container'); $time = dechex(time()); $token = md5('clip'.'1'.$this->clip->password.'0.0.0.0'.$time.$client); @@ -33,29 +33,29 @@ $token = md5('clip'.'1'.$this->clip->password.'127.0.0.1'.$time.$client); $link = '/protector/link/clip/1/'.$token.'/'.$time.'/'.$client; get($link)->assertStatus(302); - get(route('frontend.clips.show', $this->clip))->assertSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertSee('player-container'); }); it('a clip with lms acl can be accessable for clip admin', function () { $this->clip->addAcls(collect([Acl::LMS()])); - get(route('frontend.clips.show', $this->clip))->assertDontSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertDontSee('player-container'); signIn($this->clip->owner); - get(route('frontend.clips.show', $this->clip))->assertSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertSee('player-container'); }); it('a clip with lms acl can be accessable for portal admin ', function () { $this->clip->addAcls(collect([Acl::LMS()])); - get(route('frontend.clips.show', $this->clip))->assertDontSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertDontSee('player-container'); signInRole(Role::ADMIN); - get(route('frontend.clips.show', $this->clip))->assertSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertSee('player-container'); }); it('a clip with lms acl can be accessable for portal superadmin ', function () { $this->clip->addAcls(collect([Acl::LMS()])); - get(route('frontend.clips.show', $this->clip))->assertDontSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertDontSee('player-container'); signInRole(Role::SUPERADMIN); - get(route('frontend.clips.show', $this->clip))->assertSee('plyr-tides'); + get(route('frontend.clips.show', $this->clip))->assertSee('player-container'); }); diff --git a/tests/Feature/Http/Controllers/Frontend/ClipTest.php b/tests/Feature/Http/Controllers/Frontend/ShowClipsTest.php similarity index 73% rename from tests/Feature/Http/Controllers/Frontend/ClipTest.php rename to tests/Feature/Http/Controllers/Frontend/ShowClipsTest.php index fdf301f8..c2b4bad4 100644 --- a/tests/Feature/Http/Controllers/Frontend/ClipTest.php +++ b/tests/Feature/Http/Controllers/Frontend/ShowClipsTest.php @@ -1,7 +1,9 @@ mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); get(route('frontend.clips.show', $this->clip->id))->assertOk()->assertSee($this->clip->title); }); it('a guest cannot access frontend clip page if clip is not public', function () { - $this->mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $this->clip->is_public = false; $this->clip->save(); @@ -67,7 +69,7 @@ }); it('a clip owner can access frontend clip page if clip has no assets', function () { - $this->mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $emptyClip = ClipFactory::withAssets(0)->create(); signIn($emptyClip->owner); @@ -75,14 +77,14 @@ }); it('a portal admin can access frontend clip page if clip has no assets', function () { - $this->mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); signInRole(Role::ADMIN); get(route('frontend.clips.show', ClipFactory::withAssets(0)->create()))->assertOk(); }); it('a clip owner can access frontend clip page if clip is not public visible', function () { - $this->mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $clip = ClipFactory::ownedBy(signIn())->create(['is_public' => false]); signIn($clip->owner); @@ -90,7 +92,7 @@ }); it('a portal admin can access frontend clip page if clip is not public visible', function () { - $this->mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $clip = ClipFactory::create(['is_public' => false]); signInRole(Role::ADMIN); @@ -106,7 +108,7 @@ }); it('a clip owner can access frontend clip page that belongs to series that it is not public ', function () { - $this->mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $user = signIn(); $series = SeriesFactory::notPublic()->create(); $this->clip->series_id = $series->id; @@ -117,7 +119,7 @@ }); it('player in clip public page is using wowza url if wowza server is available', function () { - $this->mockHandler->append($this->mockCheckApiConnection()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); get(route('frontend.clips.show', $this->clip))->assertSee(env('WOWZA_ENGINE_URL')); }); @@ -129,26 +131,26 @@ }); it('clip edit button in clip public page is hidden for guests', function () { - $this->mockHandler->append(new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); signIn(); get(route('frontend.clips.show', $this->clip))->assertDontSee('Back to edit page'); }); it('clip comments in clip public page are hidden for guests', function () { - $this->mockHandler->append(new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); get(route('frontend.clips.show', $this->clip))->assertDontSee('Comments'); }); it('clip public page has a feeds button', function () { - $this->mockHandler->append(new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); get(route('frontend.clips.show', $this->clip))->assertSee('Feeds'); }); it('clip public page should display tags if a clip has any', function () { - $this->mockHandler->append(new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); get(route('frontend.clips.show', $this->clip))->assertDontSee('Tags'); $this->clip->addTags(collect(['single tag', 'tides', 'testTags'])); @@ -156,7 +158,7 @@ }); it('clip public page should display a series title and link if clip belongs to a series', function () { - $this->mockHandler->append(new Response(), new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $series = SeriesFactory::create(); get(route('frontend.clips.show', $this->clip))->assertDontSee($series->title); @@ -169,19 +171,39 @@ get(route('frontend.clips.show', $this->clip))->assertSee(route('frontend.series.show', $series)); }); +it('clip public page should display all alternative videos if a video has assets', function () { + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); + + Asset::factory()->create([ + 'original_file_name' => 'presentation.smil', + 'type' => Content::SMIL, + 'clip_id' => $this->clip->id, + ]); + Asset::factory()->create([ + 'original_file_name' => 'composite.smil', + 'type' => Content::SMIL, + 'clip_id' => $this->clip->id, + ]); + get(route('frontend.clips.show', $this->clip)) + ->assertSee('presenter video stream') + ->assertSee('presentation video stream') + ->assertSee('composite video stream'); +}); + it('clip public page should display clip presenters if any ', function () { - $this->mockHandler->append(new Response(), new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $presenters = Presenter::factory(2)->create(); get(route('frontend.clips.show', $this->clip))->assertDontSee('Tags'); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $this->clip->addPresenters($presenters); get(route('frontend.clips.show', $this->clip)) ->assertSee(Presenter::find(1)->getFullNameAttribute(), Presenter::find(2)->getFullNameAttribute()); }); it('clip public page should have navidate to previous and next clips if a clip belongs to a series', function () { - $this->mockHandler->append(new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); SeriesFactory::withClips(3)->withAssets(2)->create(); $clip = Clip::find(3); $previousClip = Clip::find(2); @@ -195,7 +217,7 @@ }); it('a signed in user can access frontend clip page if clip has a portal access', function () { - $this->mockHandler->append(new Response()); + $this->mockHandler->append($this->mockCheckApiConnection(), $this->mockVodSecureUrls()); $this->clip->addAcls(collect([Acl::PORTAL()])); get($this->clip->path())->assertDontSee('plyr-player'); diff --git a/tests/Setup/ClipFactory.php b/tests/Setup/ClipFactory.php index a9fdbb69..17dd1550 100644 --- a/tests/Setup/ClipFactory.php +++ b/tests/Setup/ClipFactory.php @@ -2,6 +2,7 @@ namespace Tests\Setup; +use App\Enums\Content; use App\Models\Asset; use App\Models\Clip; use App\Models\User; @@ -33,9 +34,16 @@ public function create(array $attributes = []) ] ); - Asset::factory($this->assetsCount)->create([ - 'clip_id' => $clip->id, - ]); + if ($this->assetsCount > 0) { + Asset::factory($this->assetsCount)->create([ + 'clip_id' => $clip->id, + ]); + Asset::factory()->create([ + 'original_file_name' => 'presenter.smil', + 'type' => Content::SMIL, + 'clip_id' => $clip->id, + ]); + } return $clip; } diff --git a/tests/Setup/WorksWithWowzaClient.php b/tests/Setup/WorksWithWowzaClient.php index 9163ffc8..a9af421c 100644 --- a/tests/Setup/WorksWithWowzaClient.php +++ b/tests/Setup/WorksWithWowzaClient.php @@ -31,4 +31,15 @@ public function mockCheckApiConnection(): Response '0' => 'Wowza Streaming Engine X Perpetual Edition X.X.X.xxx buildYYYVERSION', ])); } + + public function mockVodSecureUrls(): Response + { + $urls = collect([ + 'presenter' => 'localhost:9200/__def_inst/videoportal/presenter.smil', + 'presentation' => 'localhost:9200/__def_inst/videoportal/presentation.smil', + 'composite' => 'localhost:9200/__def_inst/videoportal/composite.smil', + ]); + + return new Response(200, [], json_encode([$urls])); + } } diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php index 646e9fa2..30b0ac22 100644 --- a/tests/Unit/HelpersTest.php +++ b/tests/Unit/HelpersTest.php @@ -5,7 +5,6 @@ use App\Models\Clip; use App\Models\Series; use App\Models\User; -use App\Services\WowzaService; use Facades\Tests\Setup\ClipFactory; use Facades\Tests\Setup\FileFactory; use Illuminate\Support\Facades\Storage; @@ -25,15 +24,6 @@ expect(fetchClipPoster('preview_image.png'))->toBe('/thumbnails/previews-ng/preview_image.png'); }); -it('returns a smil file for a clip', function () { - Storage::fake('videos'); - $wowzaService = app(WowzaService::class); - $wowzaService->createSmilFile($clip = ClipFactory::withAssets(2)->create(['created_at' => '01.01.2022'])); - - expect(getClipSmilFile($clip, false)) - ->toBe('http://172.17.0.2:1935/vod/_definst_/2022/01/01/TIDES_ClipID_1/presenter.smil/playlist.m3u8'); -}); - it('has a getClipStoragePath helper function for returning clip\'s date path', function () { expect(getClipStoragePath(Clip::factory()->create(['created_at' => '2021-01-13 15:38:51']))) ->toBe('/2021/01/13/TIDES_ClipID_1/');