functional show page

This commit is contained in:
Constantin Plaiasu 2024-08-26 22:05:12 +03:00
parent 6d042d8a75
commit 4831861208
13 changed files with 97 additions and 26 deletions

View file

@ -2,6 +2,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Requests\ShowRequest;
use DipeshSukhia\LaravelHtmlMinify\LaravelHtmlMinifyFacade; use DipeshSukhia\LaravelHtmlMinify\LaravelHtmlMinifyFacade;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Services\ApiClient; use App\Services\ApiClient;
@ -23,14 +24,14 @@ class ShowController extends Controller
{ {
use CleanItems, TopContent; use CleanItems, TopContent;
public function index(TmdbClient $tmdb, ApiClient $api, int $id, string $slug = null ) public function index(TmdbClient $tmdb, ApiClient $api, int $id, string $slug)
{ {
$airing = []; $airing = [];
$call = function() use($tmdb, $id, &$season, &$airing){ $call = function() use($tmdb, $id, &$season, &$airing){
$id = self::decodeId($id); $id = self::decodeId($id);
$show = $tmdb->getShow($id); $data = $tmdb->getShow($id);
$data = $this->formatTmdbShow($show);
$airing = $this->formatTmdbResponse($tmdb->getAiringShows(1), false, [], 12)['data']; $airing = $this->formatTmdbResponse($tmdb->getAiringShows(1), false, [], 12)['data'];
return $data; return $data;
}; };
@ -55,7 +56,7 @@ public function index(TmdbClient $tmdb, ApiClient $api, int $id, string $slug =
} }
public function season(TmdbClient $tmdb, ApiClient $api, int $id, int $season = null, string $slug = null ) public function season(TmdbClient $tmdb, ApiClient $api, int $id, string $slug = null, int $season = null )
{ {
$airing = []; $airing = [];
$is_show_page = is_null($season); $is_show_page = is_null($season);

View file

@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ShowRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'id' => 'numeric|required',
'slug' => 'string|required',
];
}
}

View file

@ -110,7 +110,7 @@ public static function getShowSchema(array $movie): array
return Schema::person()->name($actor['name'])->image($actor['image']); return Schema::person()->name($actor['name'])->image($actor['image']);
})->values()->all(); })->values()->all();
$movieSchema->actor($actors); $movieSchema->actor($actors);
$directors = collect($movie->crew)->filter(fn ($director) => Str::of($director['as'])->contains('producer', true))->map(function ($director) { $directors = collect($movie->crew['producers'])->filter(fn ($director) => Str::of($director['as'])->contains('producer', true))->map(function ($director) {
return Schema::person()->name($director['name'])->image($director['image']); return Schema::person()->name($director['name'])->image($director['image']);
})->values()->all(); })->values()->all();
$movieSchema->producer($directors); $movieSchema->producer($directors);

View file

@ -95,13 +95,15 @@ public function formatTmdbShow($data):array
$show['vote_count'] = $data['vote_count']; $show['vote_count'] = $data['vote_count'];
$show['number_of_episodes'] = $data['number_of_episodes']; $show['number_of_episodes'] = $data['number_of_episodes'];
$show['number_of_seasons'] = $data['number_of_seasons']; $show['number_of_seasons'] = $data['number_of_seasons'];
$show['poster'] = $this->getImageUrl($data['poster_path'], 'w500', 230, 345); $show['poster'] = $this->getImageUrl($data['poster_path'], 'w500', 300, 450);
$show['backdrop'] = $this->getImageUrl($data['backdrop_path'], 'w1280', 1280, 720); $show['backdrop'] = $this->getImageUrl($data['backdrop_path'], 'w1280', 1280, 720);
$show['backdrop_path'] = $data['backdrop_path'];
$show['first_air_date'] = $this->formatReleaseDate($data['first_air_date']); $show['first_air_date'] = $this->formatReleaseDate($data['first_air_date']);
$show['year'] = $this->formatDateYear($data['first_air_date']);
$show['genres'] = $this->formatGenres($data['genres']); $show['genres'] = $this->formatGenres($data['genres']);
$show['cast'] = $this->getCast($data['credits']['cast'] ?? [], 8); $show['cast'] = $this->getCast($data['credits']['cast'] ?? [], 8);
$show['creators'] = $this->getCreators($data['created_by'] ?? [], 8); $show['crew']['creators'] = $this->getCreators($data['created_by'] ?? [], 8);
$show['crew'] = $this->getCrew($data['credits']['crew'] ?? [], 8); $show['crew']['producers'] = $this->getCrew($data['credits']['crew'] ?? [], 8);
$show['runtime'] = isset($data['episode_run_time'][0]) ? CarbonInterval::make($data['episode_run_time'][0], 'minute')->cascade()->forHumans(['short' => true]) : false; $show['runtime'] = isset($data['episode_run_time'][0]) ? CarbonInterval::make($data['episode_run_time'][0], 'minute')->cascade()->forHumans(['short' => true]) : false;
$show['duration'] = isset($data['episode_run_time'][0]) ? CarbonInterval::make($data['episode_run_time'][0], 'minute')->totalSeconds : false; $show['duration'] = isset($data['episode_run_time'][0]) ? CarbonInterval::make($data['episode_run_time'][0], 'minute')->totalSeconds : false;
$show['countries'] = collect($data['production_countries'])->implode('name', ', '); $show['countries'] = collect($data['production_countries'])->implode('name', ', ');
@ -112,8 +114,9 @@ public function formatTmdbShow($data):array
unset($season['poster_path']); unset($season['poster_path']);
return $season; return $season;
})->values()->all(); })->values()->all();
$show['similar'] = $this->formatTmdbResponse($data['recommendations'], false, [], 6)['data'] ?? []; $show['similar'] = $this->formatTmdbResponse($data['recommendations'], false, [], 8)['data'] ?? [];
$show['keywords'] = $data['keywords']['results'] ?? []; $show['keywords'] = $data['keywords']['results'] ?? [];
$show['languages'] = self::getSpokenLanguages($data['spoken_languages']);
// dd($show); // dd($show);
return $show; return $show;

View file

@ -66,6 +66,10 @@ public static function formatReleaseDate(?string $release_date): string
{ {
return Carbon::parse($release_date)->format('M d, Y'); return Carbon::parse($release_date)->format('M d, Y');
} }
public static function formatDateYear(?string $release_date): string
{
return Carbon::parse($release_date)->format('Y');
}
public static function formatGenres(array $genres, $type = 'movie'): array public static function formatGenres(array $genres, $type = 'movie'): array
{ {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
/*! /*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/.fa,.fas,.far{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}@keyframes fa-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.fa-database:before{content:""}.fa-expand:before{content:""}.fa-expand-alt:before{content:""}.fa-expand-arrows-alt:before{content:""}.fa-eye:before{content:""}.fa-paper-plane:before{content:""}.fa-play:before{content:""}.fa-play-circle:before{content:""}.fa-playstation:before{content:""}.fa-search:before{content:""}.fa-search-dollar:before{content:""}.fa-search-location:before{content:""}.fa-search-minus:before{content:""}.fa-search-plus:before{content:""}.fa-searchengin:before{content:""}.fa-star:before{content:""}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.eot);src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.woff2) format("woff2"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.woff) format("woff"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.ttf) format("truetype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.svg#fontawesome) format("svg")}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:block;src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.eot);src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.woff2) format("woff2"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.woff) format("woff"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.ttf) format("truetype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.eot);src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.woff2) format("woff2"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.woff) format("woff"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.ttf) format("truetype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900} */.fa,.fas,.far{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}@keyframes fa-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.fa-database:before{content:""}.fa-expand:before{content:""}.fa-expand-alt:before{content:""}.fa-expand-arrows-alt:before{content:""}.fa-eye:before{content:""}.fa-paper-plane:before{content:""}.fa-play:before{content:""}.fa-play-circle:before{content:""}.fa-playstation:before{content:""}.fa-search:before{content:""}.fa-search-dollar:before{content:""}.fa-search-location:before{content:""}.fa-search-minus:before{content:""}.fa-search-plus:before{content:""}.fa-searchengin:before{content:""}.fa-star:before{content:""}.fa-unlock:before{content:""}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.eot);src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.woff2) format("woff2"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.woff) format("woff"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.ttf) format("truetype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-brands-400.svg#fontawesome) format("svg")}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:block;src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.eot);src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.woff2) format("woff2"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.woff) format("woff"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.ttf) format("truetype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.eot);src:url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.woff2) format("woff2"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.woff) format("woff"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.ttf) format("truetype"),url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900}

File diff suppressed because one or more lines are too long

View file

@ -36,7 +36,7 @@
"isEntry": true "isEntry": true
}, },
"resources/scss/app.scss": { "resources/scss/app.scss": {
"file": "assets/app-CCYkocot.css", "file": "assets/app-CT5t3asE.css",
"src": "resources/scss/app.scss", "src": "resources/scss/app.scss",
"isEntry": true "isEntry": true
}, },

View file

@ -2087,7 +2087,8 @@ a:hover {
.movie-small__thumb img { .movie-small__thumb img {
width: 100%; width: 100%;
height: 80px; height: auto;
aspect-ratio: 2/3;
min-height: 80px; min-height: 80px;
object-fit: cover; object-fit: cover;
-o-object-fit: cover; -o-object-fit: cover;
@ -2118,7 +2119,7 @@ a:hover {
.movie-list-scroll .movie-small__thumb img { .movie-list-scroll .movie-small__thumb img {
width: 100%; width: 100%;
height: 70px; // height: 70px;
object-fit: cover; object-fit: cover;
-o-object-fit: cover; -o-object-fit: cover;
object-position: center; object-position: center;

View file

@ -23,14 +23,14 @@
<div class="container position-relative"> <div class="container position-relative">
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="text-center">{{ str($show['name'])->apa() }}</h1> <h1 class="text-center">{{ str($show['title'])->apa() }}</h1>
@if($show['tagline']) @if($show['tagline'])
<p class="text-center fst-italic fs-5">"{{ $show['tagline'] }}"</p> <p class="text-center fst-italic fs-5">"{{ $show['tagline'] }}"</p>
@endif @endif
<ul class="page-breadcrumb d-flex justify-content-center"> <ul class="page-breadcrumb d-flex justify-content-center">
<li><a href="{{route('home')}}" class="">Home</a></li> <li><a href="{{route('home')}}" class="">Home</a></li>
<li><a href="{{route('movies')}}" class="">Movies</a></li> <li><a href="{{route('shows')}}" class="">Series</a></li>
<li>{{$show['name']}}</li> <li>{{$show['title']}}</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -44,10 +44,10 @@
<div class="movie-content"> <div class="movie-content">
<div class="movie-content-inner d-sm-flex justify-content-between align-items-center flex-wrap"> <div class="movie-content-inner d-sm-flex justify-content-between align-items-center flex-wrap">
<div class="movie-content-left"> <div class="movie-content-left">
<h2 class="title">Watch {{ str($show['name'])->apa() }}</h2> <h2 class="title">Watch {{ str($show['title'])->apa() }}</h2>
<span class="sub-title">Type : <span class="cat"><a href="{{route('movies',['page' => null])}}">Movie</a></span> <span class="sub-title">Type : <span class="cat"><a href="{{route('shows',['page' => null])}}">TV Show</a></span>
Category: <a href="{{route('movies.genre',['page' => null, 'genre' => $show['genres'][0]['slug']])}}">{{ $show['genres'][0]['name'] }}</a> Seasons: {{ count($show['seasons']) }}
</span> </span>
</div> </div>
<div class="movie-content-right mt-sm-0 mt-3"> <div class="movie-content-right mt-sm-0 mt-3">
@ -123,9 +123,9 @@
@endforeach</span> @endforeach</span>
</li> </li>
<li> <li>
<span class="caption">Director:</span> <span class="caption">Creator:</span>
<span class="value"> <span class="value">
@foreach($show['crew']['directors'] as $actor) @foreach($show['crew']['creators'] as $actor)
{{ $actor['name'] }}@if(!$loop->last), @endif {{ $actor['name'] }}@if(!$loop->last), @endif
@endforeach @endforeach
</span> </span>
@ -149,8 +149,40 @@
</div> </div>
</div> </div>
</div> </div>
<div class="card col-12 order-sm-2 order-1 p-0">
<div class="card-body p-0">
<div class="slimScrollDiv" style="position: relative; overflow: hidden; width: auto; height: 420px;">
<ul class="movie-small-list movie-list-scroll" style="overflow: hidden; width: auto; height: 420px;">
@foreach ($show['seasons'] as $currentSeason)
<li class="movie-small d-flex align-items-center justify-content-between movie-item__overlay video-item flex-wrap" data-img="https://script.viserlab.com/playlab/demo/assets/images/item/episode//2022/07/06/62c53342b113e1657090882.jpg" data-text="Season">
<div class="caojtyektj d-flex align-items-center flex-wrap">
<div class="movie-small__thumb">
<img src="{{ $currentSeason['poster'] }}" alt="image">
</div>
<div class="movie-small__content">
<h5>{{ $currentSeason['name'] }}</h5>
<a class="base--color" href="{{ route('show.season', ['id' => $show['id'], 'slug' => $show['slug'], 'season' => $currentSeason['season_number']]) }}">Play Now</a>
</div>
</div>
<div class="movie-small__lock">
<span class="movie-small__lock-icon">
<i class="fas fa-unlock"></i>
</span>
</div>
</li>
@endforeach
</ul>
<div class="slimScrollBar" style="background: rgb(0, 0, 0); width: 7px; position: absolute; top: 0px; opacity: 0.4; display: none; border-radius: 7px; z-index: 99; right: 1px; height: 110px;"></div><div class="slimScrollRail" style="width: 7px; height: 100%; position: absolute; top: 0px; display: none; border-radius: 7px; background: rgb(51, 51, 51); opacity: 0.2; z-index: 90; right: 1px;"></div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -231,15 +263,15 @@
{{ $actor['name'] }}@if(!$loop->last), @endif {{ $actor['name'] }}@if(!$loop->last), @endif
@endforeach @endforeach
</p> </p>
<p class="mb-1"><span class="fw-bold">Creators:</span> {{-- <p class="mb-1"><span class="fw-bold">Creators:</span>
@foreach($show['crew'] as $actor) @foreach($show['crew'] as $actor)
{{ $actor['name'] }}@if(!$loop->last), @endif {{ $actor['name'] }}@if(!$loop->last), @endif
@endforeach @endforeach
</p> </p> --}}
@isset($show['genres']) @isset($show['genres'])
<p class="mb-1"><span class="fw-bold pe-2">Genres:</span> <p class="mb-1"><span class="fw-bold pe-2">Genres:</span>
@foreach($show['genres'] as $genre) @foreach($show['genres'] as $genre)
<a href="{{route('shows.genre', $genre['slug'])}}">{{ $genre['name'] }}</a>@if(!$loop->last), @endif <span>{{ $genre['name'] }}</span>@if(!$loop->last), @endif
@endforeach @endforeach
</p> </p>
@endisset @endisset

View file

@ -33,4 +33,5 @@
Route::get('/movie/{id}/{slug}', [MovieController::class, 'index'])->name('movie'); Route::get('/movie/{id}/{slug}', [MovieController::class, 'index'])->name('movie');
Route::get('/series/{id}/{slug}', [ShowController::class, 'index'])->name('show'); Route::get('/series/{id}/{slug}', [ShowController::class, 'index'])->name('show');
Route::get('/series/{id}/{slug}/season/{season}', [ShowController::class, 'season'])->name('show.season');
Route::get('/search', [HomeController::class, 'search'])->name('search'); Route::get('/search', [HomeController::class, 'search'])->name('search');