lots of progress

This commit is contained in:
2025-01-26 21:36:05 +02:00
parent bbd082fd8f
commit 6a014c403c
44 changed files with 976 additions and 104 deletions
@@ -45,18 +45,9 @@ class FetchItemDataCommand extends Command
private function getBatch(string $type, int $batch, string $job) private function getBatch(string $type, int $batch, string $job)
{ {
return ItemToFetch::has('jobs') return ItemToFetch::whereHas('jobs', function($query) use ($job){
->with([ return $query->where('jobs', '<>', null)->whereRaw('FIND_IN_SET(?, jobs)', [$job]);
// 'jobs' => fn ($jobs) => dd($jobs->first()->remaining_jobs) /*array_search($job, $jobs->first()->remaining_jobs, true)*/, })->with(['jobs'])
'jobs' => function ($jobs) use ($job) {
$remainingJobs = $jobs->first()->remaining_jobs;
if (is_string($remainingJobs)) {
$remainingJobs = json_decode($jobs->first()->remaining_jobs, true);
}
return array_search($job, $remainingJobs, true);
},
])
->where('type', $type) ->where('type', $type)
->take($batch) ->take($batch)
->get(); ->get();
@@ -57,6 +57,7 @@ class FetchUpcomingMoviesCommand extends Command
$oldUpcomingIds = UpcomingMovie::all()->pluck('movie_id'); $oldUpcomingIds = UpcomingMovie::all()->pluck('movie_id');
foreach ($upcomingMovies['results'] as $upcomingMovie) { foreach ($upcomingMovies['results'] as $upcomingMovie) {
echo ".";
$movie = $this->process($upcomingMovie); $movie = $this->process($upcomingMovie);
if ($movie) { if ($movie) {
@@ -69,11 +70,13 @@ class FetchUpcomingMoviesCommand extends Command
if (!empty($newUpcomingIds)) { if (!empty($newUpcomingIds)) {
UpcomingMovie::whereIn('id', $oldUpcomingIds)->delete(); UpcomingMovie::whereIn('id', $oldUpcomingIds)->delete();
UpcomingMovie::insert($newUpcomingIds); UpcomingMovie::insert($newUpcomingIds);
echo "+";
} }
DB::commit(); DB::commit();
} catch (\Throwable $e) { } catch (\Throwable $e) {
DB::rollBack(); DB::rollBack();
dump($e);
} }
} }
@@ -86,14 +89,14 @@ class FetchUpcomingMoviesCommand extends Command
return $movie; return $movie;
} }
$itemToFetch = ItemToFetch::where('tmdb_id', $upcomingMovie['id'])->where('type', $type)->first(); $itemToFetch = ItemToFetch::where('tmdb_id', $upcomingMovie['id'])->where('type', ItemToFetch::TYPE_MOVIE)->first();
if ($itemToFetch) { if ($itemToFetch) {
$this->movieProcessingService->process($itemToFetch, ItemToFetchJobs::JOB_DETAILS); $this->movieProcessingService->process($itemToFetch, ItemToFetchJobs::JOB_DETAILS);
return Movie::where('tmdb_id', $upcomingMovie['id'])->first(); return Movie::where('tmdb_id', $upcomingMovie['id'])->first();
} }
ItemToFetch::create([ $itemToFetch = ItemToFetch::create([
'tmdb_id' => $upcomingMovie['id'], 'tmdb_id' => $upcomingMovie['id'],
'type' => ItemToFetch::TYPE_MOVIE, 'type' => ItemToFetch::TYPE_MOVIE,
]); ]);
@@ -107,6 +110,7 @@ class FetchUpcomingMoviesCommand extends Command
'remaining_jobs' => json_encode($jobs), 'remaining_jobs' => json_encode($jobs),
]); ]);
return null; $this->movieProcessingService->process($itemToFetch, ItemToFetchJobs::JOB_DETAILS);
return Movie::where('tmdb_id', $upcomingMovie['id'])->first();
} }
} }
@@ -88,13 +88,11 @@ class FetchExportItemsCommand extends Command
'type' => $type, 'type' => $type,
]); ]);
// $jobs = ItemToFetchJobs::JOBS;
// $jobs[] = "Details";
ItemToFetchJobs::create([ ItemToFetchJobs::create([
'tmdb_id' => $decodedLine['id'], 'tmdb_id' => $decodedLine['id'],
'type' => $type, 'type' => $type,
'remaining_jobs' => json_encode($jobs), 'remaining_jobs' => json_encode($jobs),
'jobs' => implode(',', $jobs),
]); ]);
} }
} }
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\MediaList;
use App\Models\User; use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
@@ -64,11 +65,27 @@ class RegisterController extends Controller
*/ */
protected function create(array $data) protected function create(array $data)
{ {
return User::create([ $user = User::create([
'name' => $data['name'], 'name' => $data['name'],
'slug' => Str::slug($data['name']), 'slug' => Str::slug($data['name']),
'email' => $data['email'], 'email' => $data['email'],
'password' => Hash::make($data['password']), 'password' => Hash::make($data['password']),
]); ]);
$this->createMediaList('Watchlist', $user);
$this->createMediaList('Collection', $user);
$this->createMediaList('Watched', $user);
$this->createMediaList('Liked', $user);
return $user;
}
private function createMediaList(string $name, $user)
{
return MediaList::create([
'name' => $name,
'slug' => Str::slug($name),
'user_id' => $user->id,
]);
} }
} }
@@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\MediaListRequest;
use App\Models\MediaList;
use App\Models\MediaListItem;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class MediaListController extends Controller
{
public function index()
{
$lists = MediaList::whereNotIn('name', MediaList::SPECIAL_LISTS)
->where('user_id', Auth::user()->id)
->get();
return view('profile.lists.index', compact('lists'));
}
public function show(string $userSlug, string $listSlug)
{
$user = User::where('slug', $userSlug)->first();
$list = MediaList::where('user_id', $user->id)->where('slug', $listSlug)->first();
$items = MediaListItem::where('media_list_id', $list->id)->paginate(15);
return view('profile.lists.show', compact('list', 'items'));
}
public function create()
{
return view('profile.lists.create');
}
public function store(MediaListRequest $request)
{
$slug = Str::slug($request->get('name'));
$mediaList = MediaList::create([
'description' => $request->get('description'),
'name' => $request->get('name'),
'slug' => $slug,
'user_id' => Auth::user()->id,
]);
return redirect()->route('profile.list.show', [Auth::user()->slug, $slug]);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers;
use App\Models\Movie;
use Illuminate\Http\Request;
class MovieController extends Controller
{
public function index()
{
$movies = Movie::orderBy('release_date', 'ASC')->paginate(60);
return view('movie.index', compact('movies'));
}
public function show(string $slug)
{
$movie = Movie::with('director', 'watchProviders')
->where('slug', $slug)
->firstOrFail();
return view('movie.show', compact('movie'));
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MediaListRequest 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 [
'name' => 'required|min:2|max:255',
'description' => 'sometimes|nullable',
];
}
}
@@ -2,12 +2,20 @@
namespace App\Livewire\Homepage\Movies; namespace App\Livewire\Homepage\Movies;
use App\Models\Movie;
use Livewire\Component; use Livewire\Component;
class NewMoviesComponent extends Component class NewMoviesComponent extends Component
{ {
public $newMovies;
public function render() public function render()
{ {
return view('livewire.homepage.movies.new-movies-component'); return view('livewire.homepage.movies.new-movies-component');
} }
public function mount()
{
$this->newMovies = Movie::orderBy('created_at', 'desc')->take(6)->get();
}
} }
@@ -2,12 +2,23 @@
namespace App\Livewire\Homepage\Movies; namespace App\Livewire\Homepage\Movies;
use App\Models\Movie;
use App\Models\UpcomingMovie;
use Livewire\Component; use Livewire\Component;
class UpcomingMoviesComponent extends Component class UpcomingMoviesComponent extends Component
{ {
public $upcomingMovies;
public function render() public function render()
{ {
return view('livewire.homepage.movies.upcoming-movies-component'); return view('livewire.homepage.movies.upcoming-movies-component');
} }
public function mount()
{
$this->upcomingMovies = Movie::whereIn('id', UpcomingMovie::orderBy('id', 'desc')->get()->pluck('movie_id'))
->take(6)
->get();
}
} }
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace App\Livewire\MoviePage\Tabs;
use App\Models\Movie;
use App\Models\Person;
use Livewire\Component;
class Cast extends Component
{
public $movie;
public $peopleByDepartment;
public function render()
{
return view('livewire.movie-page.tabs.cast');
}
public function mount()
{
$this->movie = Movie::with('castList')->where('id', $this->movie->id)->first();
$peopleByDepartment = [];
foreach ($this->movie->castList as $castMember) {
if (!isset($peopleByDepartment[$castMember->pivot->known_for_department])) {
$peopleByDepartment[$castMember->pivot->known_for_department] = [];
}
$peopleByDepartment[$castMember->pivot->known_for_department][] = [
Person::find($castMember->pivot->person_id),
$castMember->pivot->character,
];
}
$this->peopleByDepartment = $peopleByDepartment;
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Livewire\MoviePage\Tabs;
use Livewire\Component;
class Crew extends Component
{
public function render()
{
return view('livewire.movie-page.tabs.crew');
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace App\Livewire\MoviePage\Tabs;
use App\Models\Movie;
use App\Models\MovieAlternativeTitle;
use Livewire\Component;
class Details extends Component
{
public $movie;
public $productionCompanies;
public $productionCountries;
public $alternativeTitles;
public function render()
{
return view('livewire.movie-page.tabs.details');
}
public function mount()
{
$this->alternativeTitles = MovieAlternativeTitle::where('movie_id', $this->movie->id)->get();
$movie = Movie::with('productionCompanies', 'productionCountries')->find($this->movie->id);
$this->productionCompanies = $movie->productionCompanies;
$this->productionCountries = $movie->productionCountries;
// $this->productionCompanies = Movie::
// alt titles
// prod company
// prod country
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Livewire\MoviePage\Tabs;
use Livewire\Component;
class Releases extends Component
{
public function render()
{
return view('livewire.movie-page.tabs.releases');
}
}
+1
View File
@@ -76,6 +76,7 @@ class ItemToFetchJobs extends Model
'item_id', 'item_id',
'type', 'type',
'remaining_jobs', 'remaining_jobs',
'jobs',
]; ];
protected function casts(): array protected function casts(): array
+14
View File
@@ -6,6 +6,18 @@ use Illuminate\Database\Eloquent\Model;
class MediaList extends Model class MediaList extends Model
{ {
public const WATCHLIST = 'Watchlist';
public const COLLECTION = 'Collection';
public const WATCHED = 'Watched';
public const LIKED = 'Liked';
public const SPECIAL_LISTS = [
self::WATCHLIST,
self::COLLECTION,
self::WATCHED,
self::LIKED,
];
protected $fillable = [ protected $fillable = [
'description', 'description',
'favorites_count', 'favorites_count',
@@ -15,6 +27,8 @@ class MediaList extends Model
'iso_3166_1', 'iso_3166_1',
'list_type', 'list_type',
'name', 'name',
'slug',
'poster_path', 'poster_path',
'user_id',
]; ];
} }
+22 -1
View File
@@ -28,6 +28,17 @@ class Movie extends Model
'vote_count', 'vote_count',
]; ];
protected $casts = [
'release_date' => 'date'
];
public function director()
{
return $this->belongsToMany(Person::class, 'movie_cast', 'movie_id', 'person_id')
->where('movie_cast.known_for_department', 'Directing')
;
}
public function collections() public function collections()
{ {
return $this->belongsToMany(MovieCollection::class, 'movies_in_collection', 'movie_id', 'collection_id'); return $this->belongsToMany(MovieCollection::class, 'movies_in_collection', 'movie_id', 'collection_id');
@@ -40,7 +51,7 @@ class Movie extends Model
public function castList() public function castList()
{ {
return $this->belongsToMany(Person::class, 'movie_cast', 'movie_id', 'person_id'); return $this->belongsToMany(Person::class, 'movie_cast', 'movie_id', 'person_id')->withPivot('known_for_department', 'character', 'order');
} }
public function crewList() public function crewList()
@@ -72,4 +83,14 @@ class Movie extends Model
{ {
return $this->belongsToMany(Video::class, 'movie_videos', 'movie_id', 'video_id'); return $this->belongsToMany(Video::class, 'movie_videos', 'movie_id', 'video_id');
} }
public function productionCompanies()
{
return $this->belongsToMany(ProductionCompany::class, 'movie_production_companies', 'movie_id', 'production_company_id');
}
public function productionCountries()
{
return $this->belongsToMany(ProductionCountry::class, 'movie_production_countries', 'movie_id', 'production_country_id');
}
} }
+2 -1
View File
@@ -2,6 +2,7 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
@@ -19,6 +20,6 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
// Paginator::useBootstrapFour();
} }
} }
@@ -17,6 +17,8 @@ use App\Models\MovieCollection;
use App\Models\MovieRecommendation; use App\Models\MovieRecommendation;
use App\Models\MovieSimilar; use App\Models\MovieSimilar;
use App\Models\Person; use App\Models\Person;
use App\Models\ProductionCompany;
use App\Models\ProductionCountry;
use App\Models\ReleaseDate; use App\Models\ReleaseDate;
use App\Models\Translation; use App\Models\Translation;
use App\Models\User; use App\Models\User;
@@ -79,6 +81,14 @@ class MovieProcessingService implements ProcessingInterface
$this->processMovieGenres($movieId->id, $tmdbMovie['genres']); $this->processMovieGenres($movieId->id, $tmdbMovie['genres']);
} }
if (!empty($tmdbMovie['production_companies'])) {
$this->processProductionCompanies($movieId->id, $tmdbMovie['production_companies']);
}
if (!empty($tmdbMovie['production_countries'])) {
$this->processProductionCountries($movieId->id, $tmdbMovie['production_countries']);
}
$this->removeElementFromRemainingJobs($tmdbMovie['id'], 'Details'); $this->removeElementFromRemainingJobs($tmdbMovie['id'], 'Details');
} }
@@ -112,9 +122,11 @@ class MovieProcessingService implements ProcessingInterface
private function getCredits($item) private function getCredits($item)
{ {
echo "Credits for TMDBID ".$item->tmdb_id." \n";
$tmdbCredits = $this->moviesApi->getCredits($item->tmdb_id); $tmdbCredits = $this->moviesApi->getCredits($item->tmdb_id);
if (empty($tmdbCredits)) { if (empty($tmdbCredits)) {
echo "Empty for ".$item->tmdb_id." \n";
return; return;
} }
@@ -701,10 +713,16 @@ class MovieProcessingService implements ProcessingInterface
{ {
$itemToFetchJobs = ItemToFetchJobs::where('tmdb_id', $tmdbId)->first(); $itemToFetchJobs = ItemToFetchJobs::where('tmdb_id', $tmdbId)->first();
$remainingJobs = $itemToFetchJobs->remaining_jobs; if (!$itemToFetchJobs) {
return;
}
$remainingJobs = $itemToFetchJobs->jobs;
if (is_string($remainingJobs)) { if (is_string($remainingJobs)) {
$remainingJobs = json_decode($remainingJobs, true); $remainingJobs = explode(',', $remainingJobs);
} else {
dd($remainingJobs, $tmdbId);
} }
$remainingJobKey = array_search($job, $remainingJobs); $remainingJobKey = array_search($job, $remainingJobs);
@@ -715,13 +733,14 @@ class MovieProcessingService implements ProcessingInterface
$itemToFetchJobs->delete(); $itemToFetchJobs->delete();
} }
$itemToFetchJobs->remaining_jobs = $remainingJobs; // $itemToFetchJobs->remaining_jobs = $remainingJobs;
$itemToFetchJobs->jobs = implode(',', $remainingJobs);
$itemToFetchJobs->save(); $itemToFetchJobs->save();
} }
private function processCast($movie, array $castList) private function processCast($movie, array $castList)
{ {
if (empty($castList)) { if (empty($castList) || $movie === null) {
return; return;
} }
@@ -753,15 +772,12 @@ class MovieProcessingService implements ProcessingInterface
'place_of_birth' => $tmdbPerson['place_of_birth'], 'place_of_birth' => $tmdbPerson['place_of_birth'],
]); ]);
} }
$person = Person::create($personData); $person = Person::create($personData);
} }
if ($movie->whereHas('castList', function ($query) use ($person) { if (!$movie->whereHas('castList', function ($query) use ($person) {
$query->where('tmdb_id', '=', $person->tmdb_id); $query->where('person_id', '=', $person->id);
})->exists()) { })->exists()) {
return;
} else {
$movie->castList()->attach($person, [ $movie->castList()->attach($person, [
'known_for_department' => $castPerson['known_for_department'], 'known_for_department' => $castPerson['known_for_department'],
'character' => $castPerson['character'], 'character' => $castPerson['character'],
@@ -825,6 +841,56 @@ class MovieProcessingService implements ProcessingInterface
} }
} }
private function processProductionCompanies(int $movieId, array $productionCompanies)
{
$movie = Movie::find($movieId);
foreach ($productionCompanies as $productionCompany) {
$existingProductionCompany = ProductionCompany::where('tmdb_id', $productionCompany['id'])->first();
if (!$existingProductionCompany) {
$existingProductionCompany = ProductionCompany::create([
'tmdb_id' => $productionCompany['id'],
'logo_path' => $productionCompany['logo_path'],
'name' => $productionCompany['name'],
'origin_country' => $productionCompany['origin_country'],
]);
}
if ($movie->whereHas('productionCompanies', function ($query) use ($existingProductionCompany) {
$query->where('production_company_id', '=', $existingProductionCompany->id);
})->exists()) {
return;
} else {
$movie->productionCompanies()->attach($existingProductionCompany);
}
}
}
private function processProductionCountries(int $movieId, array $productionCountries)
{
$movie = Movie::find($movieId);
foreach ($productionCountries as $productionCountry) {
$existingProductionCountry = ProductionCountry::where('iso_3166', $productionCountry['iso_3166_1'])->first();
if (!$existingProductionCountry) {
$existingProductionCountry = ProductionCountry::create([
'iso_3166' => $productionCountry['iso_3166_1'],
'name' => $productionCountry['name'],
]);
}
if ($movie->whereHas('productionCountries', function ($query) use ($existingProductionCountry) {
$query->where('production_country_id', '=', $existingProductionCountry->id);
})->exists()) {
return;
} else {
$movie->productionCountries()->attach($existingProductionCountry);
}
}
}
private function createJob($tmdbId) private function createJob($tmdbId)
{ {
$itemToFetch = ItemToFetch::where('tmdb_id', $tmdbId)->where('type', ItemToFetch::TYPE_MOVIE)->first(); $itemToFetch = ItemToFetch::where('tmdb_id', $tmdbId)->where('type', ItemToFetch::TYPE_MOVIE)->first();
@@ -19,6 +19,7 @@ return new class extends Migration
$table->timestamp('email_verified_at')->nullable(); $table->timestamp('email_verified_at')->nullable();
$table->string('password'); $table->string('password');
$table->boolean('is_dummy_account')->default(0); $table->boolean('is_dummy_account')->default(0);
$table->string('profile_photo')->nullable();
$table->rememberToken(); $table->rememberToken();
$table->timestamps(); $table->timestamps();
}); });
@@ -17,6 +17,7 @@ return new class extends Migration
$table->bigInteger('item_id')->nullable(true); $table->bigInteger('item_id')->nullable(true);
$table->string('type')->nullable(false); $table->string('type')->nullable(false);
$table->json('remaining_jobs')->default(null); $table->json('remaining_jobs')->default(null);
$table->set('jobs', \App\Models\ItemToFetchJobs::JOBS)->default(null);
$table->timestamps(); $table->timestamps();
}); });
} }
@@ -15,13 +15,16 @@ return new class extends Migration
$table->id(); $table->id();
$table->text('description')->nullable(); $table->text('description')->nullable();
$table->integer('favorites_count')->default(0); $table->integer('favorites_count')->default(0);
$table->unsignedBigInteger('tmdb_id'); $table->unsignedBigInteger('tmdb_id')->nullable();
$table->integer('item_count')->default(0); $table->integer('item_count')->default(0);
$table->string('iso_639_1', 2); $table->string('iso_639_1', 2)->nullable();
$table->string('iso_3166_1', 2); $table->string('iso_3166_1', 2)->nullable();
$table->string('list_type', 15); $table->string('list_type', 15)->nullable();
$table->string('name', 550); $table->string('name', 550);
$table->string('slug', 550);
$table->string('poster_path', 550)->nullable(); $table->string('poster_path', 550)->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->boolean('public')->default(true);
$table->timestamps(); $table->timestamps();
}); });
} }
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('movie_production_companies', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('movie_id');
$table->unsignedBigInteger('production_company_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('movie_production_companies');
}
};
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('movie_production_countries', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('movie_id');
$table->unsignedBigInteger('production_country_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('movie_production_countries');
}
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

+65
View File
@@ -6,3 +6,68 @@
// Bootstrap // Bootstrap
@import 'bootstrap/scss/bootstrap'; @import 'bootstrap/scss/bootstrap';
.dark-mode{
}
body.light-mode{
background-color: #d9d9d9 !important;
}
.light-mode{
header{
background: white;
padding-right: 1rem;
}
.card{
margin-bottom: 1rem;
box-shadow: 0px 1px 2px -1px #918f8f;
.card-header{
background: white;
border-bottom: 1px dashed #d9d9d9;
}
.card-body{}
}
.movie-card{
border: 1px solid #c3c4c5;
background: #d9d9d9;
box-shadow: 0px 0px 3px 1px #ccc;
.quick-actions{
list-style: none;
padding: 2px 0px 7px 0px;
li{
float: left;
width: 33%;
text-align: center;
}
}
}
.movie-page{
.card{
.card-header{
h1{
display: inline-block;
font-size: 1.7em;
}
}
}
.tagline{
text-transform: uppercase;
}
.cast-list{
.department{
.persons{
.person{
img{
max-width: 45px;
margin-right: 5px;
}
margin-bottom: 30px;
}
}
}
}
}
}
+34 -14
View File
@@ -1,25 +1,45 @@
@extends('layouts.app') @extends('layouts.app')
@section('content') @section('content')
<div class="container"> <div class="container-fluid">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-8"> <div class="col-md-10">
@livewire(App\Livewire\Homepage\Movies\LatestTrailersComponent::class)
@livewire(App\Livewire\Homepage\Movies\NewMoviesComponent::class)
@livewire(App\Livewire\Homepage\Movies\UpcomingMoviesComponent::class)
<div class="card"> <div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>
<div class="card-body"> <div class="card-body">
@if (session('status')) <img src="https://a.ltrbxd.com/sm/upload/zl/t8/nl/gy/yir-banner-1900-o.png?k=caf651e2bd" alt="" class="img img-fluid">
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
</div> </div>
</div> </div>
<div class="card">
<div class="card-header">
New movies
<div class="float-end">
<a href="">view all &rarr;</a>
</div>
</div>
<div class="card-body">
@livewire(App\Livewire\Homepage\Movies\NewMoviesComponent::class)
</div>
</div>
<div class="card">
<div class="card-header">
Upcoming movies
<div class="float-end">
<a href="">view all &rarr;</a>
</div>
</div>
<div class="card-body">
@livewire(App\Livewire\Homepage\Movies\UpcomingMoviesComponent::class)
</div>
</div>
latest members
</div>
<div class="col-md-2">
right
</div> </div>
</div> </div>
</div> </div>
+6 -54
View File
@@ -12,66 +12,18 @@
<!-- Fonts --> <!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.bunny.net"> <link rel="dns-prefetch" href="//fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet"> <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">
<script src="//code.iconify.design/1/1.0.6/iconify.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet">
<!-- Scripts --> <!-- Scripts -->
@vite(['resources/sass/app.scss', 'resources/js/app.js']) @vite(['resources/sass/app.scss', 'resources/js/app.js'])
@livewireStyles @livewireStyles
</head> </head>
<body> <body class="light-mode">
<div id="app"> <div id="app">
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm"> @include('layouts.header')
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ms-auto">
<!-- Authentication Links -->
@guest
@if (Route::has('login'))
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@endif
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }}
</a>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav>
<main class="py-4"> <main class="py-4">
@yield('content') @yield('content')
+50
View File
@@ -0,0 +1,50 @@
<header class="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom">
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none">
<svg class="bi me-2" width="40" height="32"><use xlink:href="#bootstrap"></use></svg>
<span class="fs-4">Simple header</span>
</a>
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search">
<input type="search" class="form-control form-control-dark text-bg-dark" placeholder="Search..." aria-label="Search">
</form>
<ul class="nav nav-pills">
<li class="nav-item"><a href="#" class="nav-link">Home</a></li>
<li class="nav-item"><a href="{{ route('movie.index') }}" class="nav-link">Movies</a></li>
<li class="nav-item"><a href="#" class="nav-link">Tv Shows</a></li>
<li class="nav-item"><a href="#" class="nav-link">Celebrities</a></li>
<li class="nav-item"><a href="#" class="nav-link">Contact</a></li>
@guest
@if (Route::has('login'))
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@endif
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link active" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }}
</a>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<a href="{{ route('profile.list.index', Auth::user()->slug) }}" class="dropdown-item">
Lists
</a>
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</header>
@@ -1,3 +1,32 @@
<div> <div>
{{-- If you look to others for fulfillment, you will never truly be fulfilled. --}} <div class="row">
@foreach($newMovies as $newMovie)
<div class="col-md-2">
<div class="movie-card">
<a href="{{ route('movie.show', $newMovie->slug) }}" title="{{ $newMovie->title }}">
<img
src="http://image.tmdb.org/t/p/w185/{{ $newMovie->poster_path }}"
alt="{{ $newMovie->title }}"
title="{{ $newMovie->title }} ({{ $newMovie->release_date->format('Y') }})"
class="img img-fluid"
>
</a>
<ul class="quick-actions">
<li>
{{-- <span class="iconify" data-icon="mdi-check-bold"></span>--}}
<a href=""><span class="iconify" data-icon="mdi-check-outline"></span></a>
</li>
<li>
<a href=""><span class="iconify" data-icon="mdi-image-multiple"></span></a>
</li>
<li>
<a href="">
{{ $newMovie->vote_average }} <span class="iconify" data-icon="mdi-star"></span>
</a>
</li>
</ul>
</div>
</div>
@endforeach
</div>
</div> </div>
@@ -1,3 +1,32 @@
<div> <div>
{{-- In work, do what you enjoy. --}} <div class="row">
@foreach($upcomingMovies as $upcomingMovie)
<div class="col-md-2">
<div class="movie-card">
<a href="{{ route('movie.show', $upcomingMovie->slug) }}" title="{{ $upcomingMovie->title }}">
<img
src="http://image.tmdb.org/t/p/w154/{{ $upcomingMovie->poster_path }}"
alt="{{ $upcomingMovie->title }}"
title="{{ $upcomingMovie->title }} ({{ $upcomingMovie->release_date->format('Y') }})"
class="img img-fluid"
>
</a>
<ul>
<li>
{{-- <span class="iconify" data-icon="mdi-check-bold"></span>--}}
<a href=""><span class="iconify" data-icon="mdi-check-outline"></span></a>
</li>
<li>
<a href=""><span class="iconify" data-icon="mdi-image-multiple"></span></a>
</li>
<li>
<a href="">
{{ $upcomingMovie->vote_average }} <span class="iconify" data-icon="mdi-star"></span>
</a>
</li>
</ul>
</div>
</div>
@endforeach
</div>
</div> </div>
@@ -0,0 +1,31 @@
<div>
<div class="cast-list">
@foreach($peopleByDepartment as $department => $personInfo)
<div class="department">
{{ $department }}
<hr>
<div class="persons">
<div class="row">
@foreach($personInfo as [$person, $character])
<div class="col-md-3">
<div class="person">
@if($person->profile_path == null)
@if($person->gender == 2)
<img src="/img/no_avatar_male.png" alt="" class="img img-fluid float-start">
@else
<img src="/img/no_avatar_female.png" alt="" class="img img-fluid float-start">
@endif
@else
<img src="http://image.tmdb.org/t/p/w185/{{ $person->profile_path }}" alt="" class="img img-fluid float-start">
@endif
<a href="">{{ $person->name }}</a> <br>
{{ $character }}
</div>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
</div>
</div>
@@ -0,0 +1,3 @@
<div>
{{-- A good traveler has no fixed plans and is not intent upon arriving. --}}
</div>
@@ -0,0 +1,45 @@
<div>
<div class="element">
<div class="title">
Studio
</div>
<div class="content">
</div>
</div>
<div class="element">
<div class="title">
Country
</div>
<div class="content">
@foreach($productionCountries as $productionCountry)
{{ $productionCountry->name }}
@endforeach
</div>
</div>
<div class="element">
<div class="title">
Language
</div>
<div class="content">
{{ $movie->original_language }}
</div>
</div>
<div class="element">
<div class="title">
Alternative titles
</div>
<div class="content">
<ul>
@foreach($alternativeTitles as $alternativeTitle)
<li>
{{ $alternativeTitle->title }}
</li>
@endforeach
</ul>
</div>
</div>
</div>
@@ -0,0 +1,3 @@
<div>
{{-- Success is as dangerous as failure. --}}
</div>
+51
View File
@@ -0,0 +1,51 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid movie-page">
<div class="row justify-content-center">
<div class="col-md-10">
<div class="card">
<div class="card-header">Movies</div>
<div class="card-body">
<div class="row">
@foreach($movies as $movie)
<div class="col-md-2">
<div class="movie-card">
<a href="{{ route('movie.show', $movie->slug) }}" title="{{ $movie->title }}">
<img
src="http://image.tmdb.org/t/p/w185/{{ $movie->poster_path }}"
alt="{{ $movie->title }}"
title="{{ $movie->title }} @if($movie->release_date) ({{ $movie->release_date->format('Y') }})" @endif
class="img img-fluid"
>
</a>
<ul class="quick-actions">
<li>
{{-- <span class="iconify" data-icon="mdi-check-bold"></span>--}}
<a href=""><span class="iconify" data-icon="mdi-check-outline"></span></a>
</li>
<li>
<a href=""><span class="iconify" data-icon="mdi-image-multiple"></span></a>
</li>
<li>
<a href="">
{{ $movie->vote_average }} <span class="iconify" data-icon="mdi-star"></span>
</a>
</li>
</ul>
</div>
</div>
@endforeach
</div>
</div>
</div>
{{ $movies->links() }}
</div>
<div class="col-md-2">
<div class="card">
menu
</div>
</div>
</div>
</div>
@endsection
+106
View File
@@ -0,0 +1,106 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid movie-page">
<div class="row justify-content-center">
<div class="col-md-10">
<div class="card">
<div class="card-header">
<h1 class="movie-title">{{ $movie->title }}</h1> (<a href="">{{ $movie->release_date->format('Y') }}</a>)
</div>
<div class="card-body">
<div class="row">
<div class="col-md-2">
<img
src="http://image.tmdb.org/t/p/w185/{{ $movie->poster_path }}"
alt="{{ $movie->title }}"
title="{{ $movie->title }} ({{ $movie->release_date->format('Y') }})"
class="img img-fluid"
>
<div class="watch-provider">
@foreach($movie->watchProviders as $watchProvider)
<img src="http://image.tmdb.org/t/p/w45/{{ $watchProvider->logo_path }}" alt="{{ $watchProvider->provider_name }}" title="{{ $watchProvider->provider_name }}">
@endforeach
</div>
view trailer /
external ids
</div>
<div class="col-md-10">
<span>
Directed by
@foreach($movie->director as $director)
<a href="">{{ $director->name }}</a>@if(!$loop->last), @endif
@endforeach
</span>
<span>
<div class="tagline">{{ $movie->tagline }}</div>
<div class="overview">{{ $movie->overview }}</div>
</span>
<span>
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="details-tab" data-bs-toggle="tab" data-bs-target="#details-tab-pane" type="button" role="tab" aria-controls="details-tab-pane" aria-selected="true">Details</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="cast-tab" data-bs-toggle="tab" data-bs-target="#cast-tab-pane" type="button" role="tab" aria-controls="cast-tab-pane" aria-selected="false">Cast</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="contact-tab" data-bs-toggle="tab" data-bs-target="#contact-tab-pane" type="button" role="tab" aria-controls="contact-tab-pane" aria-selected="false">Crew</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="contact-tab" data-bs-toggle="tab" data-bs-target="#contact-tab-pane" type="button" role="tab" aria-controls="contact-tab-pane" aria-selected="false">Releases</button>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="details-tab-pane" role="tabpanel" aria-labelledby="details-tab" tabindex="0">
@livewire(App\Livewire\MoviePage\Tabs\Details::class, ['movie' => $movie])
</div>
<div class="tab-pane fade" id="cast-tab-pane" role="tabpanel" aria-labelledby="cast-tab" tabindex="0">
@livewire(App\Livewire\MoviePage\Tabs\Cast::class, ['movie' => $movie])
</div>
<div class="tab-pane fade" id="contact-tab-pane" role="tabpanel" aria-labelledby="contact-tab" tabindex="0">...</div>
<div class="tab-pane fade" id="disabled-tab-pane" role="tabpanel" aria-labelledby="disabled-tab" tabindex="0">...</div>
</div>
</span>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">Similar</div>
<div class="card-body"></div>
</div>
<div class="card">
<div class="card-header">Recommended</div>
<div class="card-body"></div>
</div>
<div class="card">
<div class="card-header">Write review</div>
<div class="card-body"></div>
</div>
</div>
<div class="col-md-2">
<div class="card">
<div class="user-actions">
<ul>
<li>
<a href=""><span class="iconify" data-icon="mdi-eye-check-outline"></span> Mark as seen</a>
</li>
<li>
<a href=""><span class="iconify" data-icon="mdi-thumb-up-outline"></span> Like</a>
</li>
<li>
<a href=""><span class="iconify" data-icon="mdi-playlist-check"></span> add to list</a>
</li>
</ul>
<hr>
rate
<hr>
</div>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,32 @@
@extends('profile.profile')
@section('page_content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<form action="{{ route('profile.list.store', auth()->user()->slug) }}" method="POST">
@csrf
<div class="card">
<div class="card-header">New list</div>
<div class="card-body">
<div class="form-group">
<label for="">Name</label>
<input type="text" class="form-control" name="name">
</div>
<div class="form-group">
<label for="">Description</label>
<textarea name="description" id="" cols="30" rows="10" class="form-control"></textarea>
</div>
<div class="form-group">
<label for=""><input type="checkbox" checked> Public</label>
</div>
<div class="float-end">
<input type="submit" class="btn btn-primary" value="Save!">
</div>
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,17 @@
@extends('profile.profile')
@section('page_content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
@if($lists->count())
lists
@else
<div class="medium-info-box">
No lists yet. <a href="{{ route('profile.list.create', auth()->user()->slug) }}">Start one?</a>
</div>
@endif
</div>
</div>
</div>
@endsection
+27
View File
@@ -0,0 +1,27 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="profile-nav">
avatar/name/etc <br>
<div class="menu-nav">
<ul class="list-inline">
<li class="float-start"><a href="">Profile</a></li>
<li class="float-start"><a href="">History</a></li>
<li class="float-start"><a href="">Watchlist</a></li>
<li class="float-start"><a href="">Collection</a></li>
<li class="float-start"><a href="">Ratings</a></li>
<li class="float-start"><a href="">Reviews</a></li>
<li class="float-start"><a href="">Likes</a></li>
<li class="float-start"><a href="">Network</a></li>
<li class="float-start"><a href="">Stats</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
@yield('page_content')
@endsection
+14
View File
@@ -9,3 +9,17 @@ use Illuminate\Support\Facades\Route;
Auth::routes(); Auth::routes();
Route::get('/', [App\Http\Controllers\HomeController::class, 'index'])->name('home'); Route::get('/', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::prefix('movies')->group(function () {
Route::get('/', [\App\Http\Controllers\MovieController::class, 'index'])->name('movie.index');
Route::get('/{slug}', [\App\Http\Controllers\MovieController::class, 'show'])->name('movie.show');
});
Route::prefix('profile/{slug}')->group(function () {
Route::get('/lists', [\App\Http\Controllers\MediaListController::class, 'index'])->name('profile.list.index');
Route::get('/lists/create', [\App\Http\Controllers\MediaListController::class, 'create'])->name('profile.list.create');
Route::post('/lists/store', [\App\Http\Controllers\MediaListController::class, 'store'])->name('profile.list.store');
Route::get('/lists/view/{slug}', [\App\Http\Controllers\MediaListController::class, 'show'])->name('profile.list.show');
});