65
app/Http/Controllers/AdminTitleTagsController.php
Executable file
65
app/Http/Controllers/AdminTitleTagsController.php
Executable file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Database\Datasource\Datasource;
|
||||
|
||||
class AdminTitleTagsController extends BaseController
|
||||
{
|
||||
public function index(string $tagType)
|
||||
{
|
||||
$this->authorize('index', Title::class);
|
||||
|
||||
$builder = app(modelTypeToNamespace($tagType))->newQuery();
|
||||
|
||||
$dataSource = new Datasource($builder, request()->all());
|
||||
|
||||
$pagination = $dataSource->paginate();
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function store(string $type) {
|
||||
$this->authorize('store', Title::class);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'name' => 'required|string',
|
||||
'display_name' => 'string',
|
||||
]);
|
||||
|
||||
$tag = app(modelTypeToNamespace($type))->create($data);
|
||||
|
||||
return $this->success(['tag' => $tag]);
|
||||
}
|
||||
|
||||
public function update(string $type, int $tagId) {
|
||||
$this->authorize('update', Title::class);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'name' => 'string',
|
||||
'display_name' => 'string',
|
||||
]);
|
||||
|
||||
$tag = app(modelTypeToNamespace($type))->findOrFail($tagId);
|
||||
|
||||
$tag->update($data);
|
||||
|
||||
return $this->success(['tag' => $tag]);
|
||||
}
|
||||
|
||||
public function destroy($type, string $ids)
|
||||
{
|
||||
$tagIds = explode(',', $ids);
|
||||
$this->authorize('destroy', Title::class);
|
||||
|
||||
foreach ($tagIds as $tagId) {
|
||||
$tag = app(modelTypeToNamespace($type))->findOrFail($tagId);
|
||||
$tag->titles()->detach();
|
||||
$tag->delete();
|
||||
}
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
297
app/Http/Controllers/ApiTmdbController.php
Executable file
297
app/Http/Controllers/ApiTmdbController.php
Executable file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
use App\Models\Genre;
|
||||
use App\Models\ProductionCountry;
|
||||
use App\Models\Title;
|
||||
use App\Models\Image;
|
||||
use App\Models\Keyword;
|
||||
use App\Models\Person;
|
||||
use App\Models\Video;
|
||||
use Intervention\Image\Drivers\Gd\Driver;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ApiTmdbController extends BaseController
|
||||
{
|
||||
|
||||
protected $baseUrl = 'https://api.themoviedb.org/3';
|
||||
protected $token = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjYTJiNzZmNDkyZWQxNGFmMmU2Njk4N2E2YmRjZDY0ZiIsIm5iZiI6MTc0OTQ2MzY4NC41NjksInN1YiI6IjY4NDZiMjg0NDQ5MDEyOGMxMzNmYzk1NiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.PXMuq0fxXaC2kR1qrd-LDeezz0HAhL8o3-4QGaik0D4";
|
||||
|
||||
private array $sizes = [
|
||||
'original' => null,
|
||||
'large' => 500,
|
||||
'medium' => 300,
|
||||
'small' => 92,
|
||||
];
|
||||
|
||||
public function index()
|
||||
{
|
||||
$movieId = request('movieId');
|
||||
//$movieId = 1284120;
|
||||
|
||||
/****************************************** */
|
||||
$responseMovie = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->token,
|
||||
'Accept' => 'application/json',
|
||||
])->get("$this->baseUrl/movie/$movieId", [
|
||||
'language' => 'fr-FR'
|
||||
]);
|
||||
|
||||
$movie = $responseMovie->successful() ? $responseMovie->json() : [];
|
||||
|
||||
|
||||
$title = Title::create([
|
||||
'imdb_id' => $movie['id'],
|
||||
'backdrop' => "https://image.tmdb.org/t/p/original".$movie['backdrop_path'],
|
||||
'poster' => "https://image.tmdb.org/t/p/original".$movie['poster_path'],
|
||||
'name' => $movie['title'],
|
||||
'original_title' => $movie['original_title'],
|
||||
'language' => $movie['original_language'],
|
||||
'is_series' => false,
|
||||
'release_date' => $movie['release_date'],
|
||||
'tagline' => $movie['tagline'],
|
||||
'description' => $movie['overview'],
|
||||
'runtime' => $movie['runtime'],
|
||||
//////////////////'certification' => $movie[''],
|
||||
'budget' => $movie['budget'],
|
||||
'revenue' => $movie['revenue'],
|
||||
'popularity' => $movie['popularity'],
|
||||
'language' => $movie['original_language'],
|
||||
'adult' => $movie['adult'],
|
||||
'type' => 'movie',
|
||||
'tmdb_vote_average' => $movie['vote_average']
|
||||
]);
|
||||
|
||||
|
||||
$genres = $countrys = [];
|
||||
|
||||
foreach($movie['production_countries'] as $country)
|
||||
{
|
||||
$productionCountry = ProductionCountry::updateOrCreate(
|
||||
['name' => $country['iso_3166_1']],
|
||||
['name' => $country['iso_3166_1'], 'display_name' => $country['name']
|
||||
]);
|
||||
$countrys[] = $productionCountry->id;
|
||||
}
|
||||
|
||||
foreach($movie['genres'] as $genre)
|
||||
{
|
||||
$genre = Genre::updateOrCreate(
|
||||
['name' => $genre['name']],
|
||||
[
|
||||
'name' => $genre['name'],
|
||||
'display_name' => $genre['name']
|
||||
]);
|
||||
$genres[] = $genre->id;
|
||||
}
|
||||
|
||||
$title->productionCountries()->attach($countrys);
|
||||
$title->genres()->attach($genres);
|
||||
|
||||
/*********************************************** */
|
||||
$responseMovieKeyword = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->token,
|
||||
'Accept' => 'application/json',
|
||||
])->get("$this->baseUrl/movie/$movieId/keywords", [
|
||||
'language' => 'fr-FR'
|
||||
]);
|
||||
|
||||
$movieKeywords = $responseMovieKeyword->successful() ? $responseMovieKeyword->json() : [];
|
||||
|
||||
$keywords = [];
|
||||
foreach($movieKeywords['keywords'] as $valKey)
|
||||
{
|
||||
$keyword = Keyword::updateOrCreate(
|
||||
['name' => $valKey['name']],
|
||||
[
|
||||
'name' => $valKey['name'],
|
||||
'display_name' => $valKey['name']
|
||||
]);
|
||||
$keywords[] = $keyword->id;
|
||||
}
|
||||
$title->keywords()->attach($keywords);
|
||||
|
||||
/************************************************/
|
||||
|
||||
$responseMovieCredits = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->token,
|
||||
'Accept' => 'application/json',
|
||||
])->get("$this->baseUrl/movie/$movieId/credits", [
|
||||
'language' => 'fr-FR'
|
||||
]);
|
||||
|
||||
$movieCredits = $responseMovieCredits->successful() ? $responseMovieCredits->json() : [];
|
||||
|
||||
|
||||
foreach($movieCredits['cast'] as $cast)
|
||||
{
|
||||
$person_id = $this->getPeopleByTMDBid($cast['id']);
|
||||
$title->createCredit([
|
||||
'person_id'=>$person_id,
|
||||
'department'=> "actors",
|
||||
'job'=>"actor",
|
||||
'order'=>$cast['order'],
|
||||
'character'=>$cast['character'],
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
foreach($movieCredits['crew'] as $cast)
|
||||
{
|
||||
$person_id = $this->getPeopleByTMDBid($cast['id']);
|
||||
$title->createCredit([
|
||||
'person_id'=>$person_id,
|
||||
'department'=> $cast['department'],
|
||||
'job'=>$cast['job'],
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
/********************* Videos *******************************/
|
||||
|
||||
$responseMovieVideos = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->token,
|
||||
'Accept' => 'application/json',
|
||||
])->get("$this->baseUrl/movie/$movieId/videos", [
|
||||
'language' => 'fr-FR'
|
||||
]);
|
||||
|
||||
$MovieVideos = $responseMovieVideos->successful() ? $responseMovieVideos->json() : [];
|
||||
foreach($MovieVideos['results'] as $keyV =>$video)
|
||||
{
|
||||
if( ($video['site'] == "YouTube") && ($video['type'] == "Trailer") )
|
||||
{
|
||||
Video::create([
|
||||
'name'=>$video['name'],
|
||||
'src'=>"https://youtube.com/embed/".$video['key'],
|
||||
'type'=>'embed',
|
||||
'quality'=> ($video['size']==1080)? "1080p" : null,
|
||||
'title_id'=>$title->id,
|
||||
'origin'=>"local",
|
||||
'downvotes'=>0,
|
||||
'upvotes'=>0,
|
||||
'approved'=>1,
|
||||
'order'=>$keyV,
|
||||
'user_id'=>auth()->id(),
|
||||
'language'=>$video['iso_639_1'],
|
||||
'category'=>'trailer',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************Images***************************/
|
||||
|
||||
$responseMovieImages = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->token,
|
||||
'Accept' => 'application/json',
|
||||
])->get("$this->baseUrl/movie/$movieId/images", []);
|
||||
|
||||
$movieImages = $responseMovieImages->successful() ? $responseMovieImages->json() : [];
|
||||
for($i=0; $i<=5; $i++)
|
||||
{
|
||||
$nameFileImage = isset($movieImages['backdrops'][$i]['file_path'])? $movieImages['backdrops'][$i]['file_path'] : null;
|
||||
if($nameFileImage)
|
||||
{
|
||||
$url = $this->saveImage("https://image.tmdb.org/t/p/original$nameFileImage");
|
||||
|
||||
Image::where('model_type', Title::MODEL_TYPE)
|
||||
->where('model_id', $title->id)
|
||||
->increment('order');
|
||||
|
||||
Image::create([
|
||||
'url' => $url,
|
||||
'type' => 'backdrop',
|
||||
'source' => 'local',
|
||||
'model_type' => Title::MODEL_TYPE,
|
||||
'model_id' => $title->id,
|
||||
'order' => 0
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getPeopleByTMDBid($tmdb_id)
|
||||
{
|
||||
$person = DB::table('people')->where('tmdb_id', $tmdb_id)->first();
|
||||
|
||||
if(!$person)
|
||||
{
|
||||
$responsePeople = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->token,
|
||||
'Accept' => 'application/json',
|
||||
])->get("$this->baseUrl/person/$tmdb_id", [
|
||||
'language' => 'fr-FR'
|
||||
]);
|
||||
|
||||
$people = $responsePeople->successful() ? $responsePeople->json() : [];
|
||||
$person = Person::create([
|
||||
'tmdb_id' => $people['id'],
|
||||
'name' => $people['name'],
|
||||
'description'=>$people['biography'],
|
||||
'gender'=>$people['gender'],
|
||||
'birth_date'=>$people['birthday'],
|
||||
'birth_place'=>$people['place_of_birth'],
|
||||
'poster'=>"https://image.tmdb.org/t/p/w500".$people['profile_path'],
|
||||
'imdb_id'=>$people['imdb_id'],
|
||||
'known_for'=>$people['known_for_department'],
|
||||
'popularity'=>$people['popularity'],
|
||||
'death_date'=>$people['deathday'],
|
||||
'adult'=>$people['adult'],
|
||||
]);
|
||||
}
|
||||
return $person->id ;
|
||||
}
|
||||
|
||||
|
||||
public function saveImage(string $urlImage): string
|
||||
{
|
||||
$hash = Str::random(30);
|
||||
$manager = new ImageManager(new Driver());
|
||||
|
||||
// Télécharger l'image depuis l'URL
|
||||
$imageContent = file_get_contents($urlImage);
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'img');
|
||||
file_put_contents($tempFile, $imageContent);
|
||||
|
||||
try {
|
||||
$img = $manager->read($tempFile);
|
||||
|
||||
// Déterminer l'extension à partir de l'URL ou utiliser jpeg par défaut
|
||||
$pathInfo = pathinfo(parse_url($urlImage, PHP_URL_PATH));
|
||||
$extension = $pathInfo['extension'] ?? 'jpeg';
|
||||
$extension = in_array(strtolower($extension), ['jpg', 'jpeg', 'png']) ? $extension : 'jpeg';
|
||||
|
||||
foreach ($this->sizes as $key => $size) {
|
||||
if ($size) {
|
||||
$img->scale($size);
|
||||
}
|
||||
|
||||
Storage::disk('public')->put(
|
||||
"media-images/backdrops/$hash/$key.$extension",
|
||||
$extension === 'png' ? $img->toPng() : $img->toJpeg(),
|
||||
);
|
||||
}
|
||||
|
||||
$endpoint = config('common.site.file_preview_endpoint');
|
||||
$uri = "media-images/backdrops/$hash/original.$extension";
|
||||
return $endpoint
|
||||
? "$endpoint/storage/$uri"
|
||||
: Storage::disk('public')->url($uri);
|
||||
} finally {
|
||||
// Nettoyer le fichier temporaire
|
||||
if (file_exists($tempFile)) {
|
||||
unlink($tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
app/Http/Controllers/AppHomeController.php
Executable file
38
app/Http/Controllers/AppHomeController.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Title;
|
||||
use Auth;
|
||||
use Common\Billing\Models\Product;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class AppHomeController extends BaseController
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
if (
|
||||
settings('homepage.type') === 'channels' ||
|
||||
(Auth::check() && settings('homepage.type') === 'landingPage')
|
||||
) {
|
||||
return app(FallbackRouteController::class)->renderChannel(
|
||||
settings('homepage.value'),
|
||||
);
|
||||
} else {
|
||||
return $this->renderClientOrApi([
|
||||
'pageName' => 'landing-page',
|
||||
'data' => [
|
||||
'loader' => 'landingPage',
|
||||
'products' => Product::with(['permissions', 'prices'])
|
||||
->limit(15)
|
||||
->orderBy('position', 'asc')
|
||||
->get(),
|
||||
'trendingTitles' => Title::orderBy('popularity', 'desc')
|
||||
->compact()
|
||||
->limit(6)
|
||||
->get(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
app/Http/Controllers/ChannelItemController.php
Executable file
50
app/Http/Controllers/ChannelItemController.php
Executable file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Channel;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ChannelItemController extends BaseController
|
||||
{
|
||||
public function add(Channel $channel)
|
||||
{
|
||||
$this->authorize('update', $channel);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'itemId' => 'required|integer',
|
||||
'itemType' => 'required|string',
|
||||
]);
|
||||
|
||||
$relationName = Str::plural($data['itemType']);
|
||||
|
||||
$channel->$relationName()->sync(
|
||||
[
|
||||
$data['itemId'] => [
|
||||
'order' => $channel->$relationName()->count() + 1,
|
||||
],
|
||||
],
|
||||
false,
|
||||
);
|
||||
$channel->touch();
|
||||
|
||||
return $this->success(['channel' => $channel]);
|
||||
}
|
||||
|
||||
public function remove(Channel $channel)
|
||||
{
|
||||
$this->authorize('update', $channel);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'itemId' => 'required|integer',
|
||||
'itemType' => 'required|string',
|
||||
]);
|
||||
|
||||
$relationName = Str::plural($data['itemType']);
|
||||
$channel->$relationName()->detach($data['itemId']);
|
||||
$channel->touch();
|
||||
|
||||
return $this->success(['channel' => $channel]);
|
||||
}
|
||||
}
|
||||
104
app/Http/Controllers/EpisodeController.php
Executable file
104
app/Http/Controllers/EpisodeController.php
Executable file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Loaders\EpisodeLoader;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class EpisodeController extends BaseController
|
||||
{
|
||||
public function show()
|
||||
{
|
||||
$data = (new EpisodeLoader())->loadData(request('loader'));
|
||||
|
||||
$this->authorize('show', $data['title']);
|
||||
|
||||
return $this->renderClientOrApi([
|
||||
'data' => $data,
|
||||
'pageName' => 'episode-page',
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Title $title, int $seasonNumber, int $episodeNumber)
|
||||
{
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$episode = $title
|
||||
->episodes()
|
||||
->where('season_number', $seasonNumber)
|
||||
->where('episode_number', $episodeNumber)
|
||||
->firstOrFail();
|
||||
|
||||
$this->validate(request(), [
|
||||
'episode_number' => [
|
||||
'integer',
|
||||
Rule::unique('episodes')
|
||||
->ignore($episode->episode_number, 'episode_number')
|
||||
->where(function (Builder $query) use ($episode) {
|
||||
$query
|
||||
->where('season_number', $episode->season_number)
|
||||
->where('title_id', $episode->title_id);
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
||||
$episode->fill(request()->all())->save();
|
||||
|
||||
return $this->success(['episode' => $episode]);
|
||||
}
|
||||
|
||||
public function store(Title $title, int $seasonNumber)
|
||||
{
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$season = $title->findSeason($seasonNumber)->loadCount('episodes');
|
||||
|
||||
$this->validate(request(), [
|
||||
'episode_number' => [
|
||||
'integer',
|
||||
Rule::unique('episodes')->where(function (Builder $query) use (
|
||||
$season,
|
||||
) {
|
||||
$query
|
||||
->where('season_number', $season->number)
|
||||
->where('title_id', $season->title_id);
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
||||
$epNum = request('episode_number');
|
||||
if (!$epNum) {
|
||||
$epNum =
|
||||
$season
|
||||
->episodes()
|
||||
->orderBy('episode_number', 'desc')
|
||||
->value('episode_number') + 1;
|
||||
}
|
||||
|
||||
$episode = Episode::create(
|
||||
array_merge(request()->all(), [
|
||||
'season_number' => $season->number,
|
||||
'episode_number' => $epNum,
|
||||
'season_id' => $season->id,
|
||||
'title_id' => $season->title_id,
|
||||
]),
|
||||
);
|
||||
|
||||
return $this->success(['episode' => $episode]);
|
||||
}
|
||||
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$this->authorize('destroy', Title::class);
|
||||
|
||||
$episode = Episode::findOrFail($id);
|
||||
$episode->credits()->detach();
|
||||
$episode->delete();
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
62
app/Http/Controllers/FallbackRouteController.php
Executable file
62
app/Http/Controllers/FallbackRouteController.php
Executable file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Channel;
|
||||
use Common\Channels\ChannelController;
|
||||
use Common\Core\Controllers\HomeController;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class FallbackRouteController
|
||||
{
|
||||
static array $defaultRoutes = ['lists', 'titles', 'search', 'news', 'user'];
|
||||
|
||||
public function __invoke(string $path)
|
||||
{
|
||||
$parts = explode('/', $path);
|
||||
|
||||
if (
|
||||
count($parts) > 2 ||
|
||||
count($parts) < 1 ||
|
||||
(count($parts) === 1 && in_array($parts[0], self::$defaultRoutes))
|
||||
) {
|
||||
return $this->renderClient();
|
||||
}
|
||||
|
||||
// first try to match a channel, if none is found, fallback to rendering client side app
|
||||
try {
|
||||
if ($parts[0] === 'lists' && isset($parts[1])) {
|
||||
request()->merge(['channelType' => 'list']);
|
||||
$parts[0] = $parts[1];
|
||||
$parts[1] = null;
|
||||
}
|
||||
|
||||
if ($parts[0] === 'channel' && isset($parts[1])) {
|
||||
$parts[0] = $parts[1];
|
||||
$parts[1] = null;
|
||||
}
|
||||
|
||||
$slugOrId = $parts[0];
|
||||
$restriction = $parts[1] ?? null;
|
||||
return $this->renderChannel($slugOrId, $restriction);
|
||||
} catch (ModelNotFoundException) {
|
||||
return $this->renderClient();
|
||||
}
|
||||
}
|
||||
|
||||
public function renderChannel(string $slugOrId, ?string $restriction = null)
|
||||
{
|
||||
$channel = app(Channel::class)->resolveRouteBinding($slugOrId);
|
||||
if ($restriction) {
|
||||
request()->merge(['restriction' => $restriction]);
|
||||
}
|
||||
return app(ChannelController::class)->show($channel);
|
||||
}
|
||||
|
||||
public function renderClient()
|
||||
{
|
||||
// no need to prerender channels here, use base HomeController
|
||||
request()->route()->action['uses'] = HomeController::class . '@show';
|
||||
return app(HomeController::class)->show();
|
||||
}
|
||||
}
|
||||
43
app/Http/Controllers/ImageOrderController.php
Executable file
43
app/Http/Controllers/ImageOrderController.php
Executable file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
use DB;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ImageOrderController extends BaseController
|
||||
{
|
||||
public function __construct(private Title $title, private Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $titleId
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function changeOrder($titleId) {
|
||||
|
||||
$title = $this->title->findOrFail($titleId);
|
||||
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$this->validate($this->request, [
|
||||
'ids' => 'array|min:1',
|
||||
'ids.*' => 'integer'
|
||||
]);
|
||||
|
||||
$queryPart = '';
|
||||
foreach($this->request->get('ids') as $order => $id) {
|
||||
$queryPart .= " when id=$id then $order";
|
||||
}
|
||||
|
||||
DB::table('images')
|
||||
->whereIn('id', $this->request->get('ids'))
|
||||
->update(['order' => DB::raw("(case $queryPart end)")]);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
67
app/Http/Controllers/ImagesController.php
Executable file
67
app/Http/Controllers/ImagesController.php
Executable file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Titles\StoreMediaImageOnDisk;
|
||||
use App\Models\Image;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
use Storage;
|
||||
|
||||
class ImagesController extends BaseController
|
||||
{
|
||||
public function store()
|
||||
{
|
||||
$titleId = request('titleId');
|
||||
$model = app(Title::class)->findOrFail($titleId);
|
||||
|
||||
$this->authorize('store', $model);
|
||||
|
||||
$this->validate(request(), [
|
||||
'file' => 'required|image|max:10240',
|
||||
'titleId' => 'required|integer',
|
||||
]);
|
||||
|
||||
$url = app(StoreMediaImageOnDisk::class)->execute(
|
||||
request()->file('file'),
|
||||
);
|
||||
|
||||
// put new image at the start of the list when sorted by "order"
|
||||
Image::where('model_type', Title::MODEL_TYPE)
|
||||
->where('model_id', $titleId)
|
||||
->increment('order');
|
||||
|
||||
$image = Image::create([
|
||||
'url' => $url,
|
||||
'type' => 'backdrop',
|
||||
'source' => 'local',
|
||||
'model_type' => Title::MODEL_TYPE,
|
||||
'model_id' => $titleId,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
return $this->success(['image' => $image]);
|
||||
}
|
||||
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$img = Image::findOrFail($id);
|
||||
$model = app(modelTypeToNamespace($img->model_type))->findOrFail(
|
||||
$img->model_id,
|
||||
);
|
||||
|
||||
$this->authorize('destroy', $model);
|
||||
|
||||
if ($img->source === 'local') {
|
||||
// storage/media-images/backdrops/kw4q4eg5g8q4eq6/original.jpg
|
||||
$dir = str_replace('storage/', '', dirname($img->url));
|
||||
if (Storage::disk('public')->exists($dir)) {
|
||||
Storage::disk('public')->deleteDirectory($dir);
|
||||
}
|
||||
}
|
||||
|
||||
$img->delete();
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
122
app/Http/Controllers/ImportMediaController.php
Executable file
122
app/Http/Controllers/ImportMediaController.php
Executable file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Person;
|
||||
use App\Models\Title;
|
||||
use App\Services\Data\Tmdb\TmdbApi;
|
||||
use Carbon\Carbon;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class ImportMediaController extends BaseController
|
||||
{
|
||||
public function importMediaItem()
|
||||
{
|
||||
$this->authorize('store', Title::class);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'media_type' => 'required|string',
|
||||
'tmdb_id' => 'required|integer',
|
||||
]);
|
||||
|
||||
if ($data['media_type'] === Person::MODEL_TYPE) {
|
||||
$mediaItem = Person::withoutGlobalScope('adult')
|
||||
->firstOrCreate([
|
||||
'tmdb_id' => $data['tmdb_id'],
|
||||
])
|
||||
->maybeUpdateFromExternal([
|
||||
'forceAutomation' => true,
|
||||
'ignoreLastUpdate' => true,
|
||||
]);
|
||||
} else {
|
||||
$mediaItem = Title::withoutGlobalScope('adult')
|
||||
->firstOrCreate([
|
||||
'tmdb_id' => $data['tmdb_id'],
|
||||
'is_series' => $data['media_type'] === Title::SERIES_TYPE,
|
||||
])
|
||||
->maybeUpdateFromExternal([
|
||||
'forceAutomation' => true,
|
||||
'updateLast3Seasons' => true,
|
||||
'ignoreLastUpdate' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
if (!$mediaItem) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return ['mediaItem' => $mediaItem];
|
||||
}
|
||||
|
||||
public function importViaBrowse()
|
||||
{
|
||||
$this->authorize('store', Title::class);
|
||||
|
||||
if (!config('services.tmdb.key')) {
|
||||
abort(
|
||||
403,
|
||||
'Enter your Themoviedb API key in settings page before importing titles.',
|
||||
);
|
||||
}
|
||||
|
||||
@set_time_limit(0);
|
||||
@ini_set('memory_limit', '200M');
|
||||
|
||||
$tmdbParams = [
|
||||
'with_release_type' => '2|3',
|
||||
];
|
||||
|
||||
if (request('country')) {
|
||||
$tmdbParams['with_origin_country'] = strtolower(request('country'));
|
||||
}
|
||||
if (request('language')) {
|
||||
$tmdbParams['with_original_language'] = request('language');
|
||||
}
|
||||
if (request('min_rating')) {
|
||||
$tmdbParams['vote_average.gte'] = request('min_rating');
|
||||
}
|
||||
if (request('max_rating')) {
|
||||
$tmdbParams['vote_average.lte'] = request('max_rating');
|
||||
}
|
||||
if (request('genres')) {
|
||||
$tmdbParams['with_genres'] = request('genres');
|
||||
}
|
||||
if (request('keywords')) {
|
||||
$tmdbParams['with_keywords'] = request('keywords');
|
||||
}
|
||||
if (request('start_date') && request('end_date')) {
|
||||
$tmdbParams['release_date.gte'] = Carbon::parse(
|
||||
request('start_date'),
|
||||
)->format('Y-m-d');
|
||||
$tmdbParams['release_date.lte'] = Carbon::parse(
|
||||
request('end_date'),
|
||||
)->format('Y-m-d');
|
||||
}
|
||||
|
||||
$response = app(TmdbApi::class)->browse(
|
||||
request('current_page', 1),
|
||||
request('type', 'movie'),
|
||||
$tmdbParams,
|
||||
);
|
||||
|
||||
$titles = $response['results']
|
||||
->map(function ($result) {
|
||||
return Title::withoutGlobalScope('adult')
|
||||
->firstOrCreate([
|
||||
'tmdb_id' => $result['tmdb_id'],
|
||||
'is_series' => $result['is_series'],
|
||||
])
|
||||
->maybeUpdateFromExternal([
|
||||
'forceAutomation' => true,
|
||||
]);
|
||||
})
|
||||
->filter()
|
||||
->values();
|
||||
|
||||
return [
|
||||
'titles' => $titles,
|
||||
'total_pages' => $response['total_pages'],
|
||||
'current_page' => request('current_page', 1),
|
||||
];
|
||||
}
|
||||
}
|
||||
24
app/Http/Controllers/InsightsReportController.php
Executable file
24
app/Http/Controllers/InsightsReportController.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Plays\BuildPlaysReport;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class InsightsReportController extends BaseController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// will authorize based on specified model in "BuildInsightsReport"
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function __invoke()
|
||||
{
|
||||
$report = app(BuildPlaysReport::class)->execute(
|
||||
request()->all(),
|
||||
);
|
||||
|
||||
return $this->success(['report' => $report]);
|
||||
}
|
||||
}
|
||||
19
app/Http/Controllers/ListsController.php
Executable file
19
app/Http/Controllers/ListsController.php
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Lists\ListsLoader;
|
||||
use App\Models\Channel;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class ListsController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', [Channel::class, 'list']);
|
||||
|
||||
$pagination = (new ListsLoader())->allLists(request()->all());
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
}
|
||||
127
app/Http/Controllers/NewsController.php
Executable file
127
app/Http/Controllers/NewsController.php
Executable file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\News\ImportNewsFromRemoteProvider;
|
||||
use App\Models\NewsArticle;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Database\Datasource\Datasource;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class NewsController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('show', NewsArticle::class);
|
||||
|
||||
$paginator = new Datasource(NewsArticle::query(), request()->all());
|
||||
|
||||
$pagination = $paginator->paginate();
|
||||
|
||||
if (request('stripHtml') || request('truncateBody')) {
|
||||
$pagination
|
||||
->map(function (NewsArticle $article) {
|
||||
if (request('stripHtml')) {
|
||||
// remove html tags
|
||||
$article->body = strip_tags($article->body);
|
||||
|
||||
// remove last "...see full article"
|
||||
$parts = explode('...', $article->body);
|
||||
if (
|
||||
count($parts) > 1 &&
|
||||
Str::contains(last($parts), 'See full article')
|
||||
) {
|
||||
array_pop($parts);
|
||||
}
|
||||
$article->body = implode('', $parts);
|
||||
}
|
||||
|
||||
if ($newLength = (int) request('truncateBody')) {
|
||||
$article->body = Str::limit($article->body, $newLength);
|
||||
}
|
||||
|
||||
return $article;
|
||||
})
|
||||
->values();
|
||||
}
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function show($slugOrId)
|
||||
{
|
||||
$article = NewsArticle::where('id', $slugOrId)
|
||||
->orWhere('slug', $slugOrId)
|
||||
->firstOrFail();
|
||||
|
||||
$this->authorize('show', $article);
|
||||
|
||||
$data = [
|
||||
'article' => $article,
|
||||
'related' => NewsArticle::compact()
|
||||
->where('id', '!=', $article->id)
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit(10)
|
||||
->get(),
|
||||
'loader' => 'newsArticlePage',
|
||||
];
|
||||
|
||||
return $this->renderClientOrApi([
|
||||
'pageName' => 'news-article-page',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
$article = NewsArticle::findOrFail($id);
|
||||
|
||||
$this->authorize('update', $article);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'title' => 'min:5|max:250',
|
||||
'body' => 'min:5',
|
||||
'image' => 'string',
|
||||
'slug' => 'string',
|
||||
]);
|
||||
|
||||
$article->fill($data)->save();
|
||||
|
||||
return $this->success(['article' => $article]);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
$this->authorize('store', NewsArticle::class);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'title' => 'required|min:5|max:250',
|
||||
'body' => 'required|min:5',
|
||||
'image' => 'string',
|
||||
'slug' => 'required|string',
|
||||
]);
|
||||
|
||||
$article = NewsArticle::create($data);
|
||||
|
||||
return $this->success(['article' => $article]);
|
||||
}
|
||||
|
||||
public function destroy(string $ids)
|
||||
{
|
||||
$ids = explode(',', $ids);
|
||||
$this->authorize('destroy', NewsArticle::class);
|
||||
|
||||
NewsArticle::whereIn('id', $ids)->delete();
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function importFromRemoteProvider()
|
||||
{
|
||||
$this->authorize('store', NewsArticle::class);
|
||||
|
||||
app(ImportNewsFromRemoteProvider::class)->execute();
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
86
app/Http/Controllers/PersonController.php
Executable file
86
app/Http/Controllers/PersonController.php
Executable file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\People\DeletePeople;
|
||||
use App\Actions\People\GetPersonCredits;
|
||||
use App\Actions\People\PaginatePeople;
|
||||
use App\Jobs\IncrementModelViews;
|
||||
use App\Models\Person;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class PersonController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', Person::class);
|
||||
|
||||
$pagination = (new PaginatePeople())->execute(request()->all());
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function show($id, $name = null)
|
||||
{
|
||||
$this->authorize('show', Person::class);
|
||||
|
||||
$loader = request('loader', 'personPage');
|
||||
|
||||
if (is_numeric($id) || ctype_digit($id)) {
|
||||
$person = Person::findOrFail($id);
|
||||
} else {
|
||||
$person = Person::firstOrCreateFromEncodedTmdbId($id);
|
||||
}
|
||||
|
||||
if ($loader === 'personPage' && requestIsFromFrontend()) {
|
||||
$person->maybeUpdateFromExternal();
|
||||
}
|
||||
|
||||
$data = array_merge(
|
||||
['person' => $person, 'loader' => $loader],
|
||||
app(GetPersonCredits::class)->execute($person),
|
||||
);
|
||||
|
||||
(new IncrementModelViews())->execute($data['person']);
|
||||
|
||||
return $this->renderClientOrApi([
|
||||
'data' => $data,
|
||||
'pageName' => 'person-page',
|
||||
]);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
$this->authorize('store', Person::class);
|
||||
|
||||
$data = request()->all();
|
||||
$data['popularity'] = Arr::get($data, 'popularity') ?: 50;
|
||||
$person = Person::create($data);
|
||||
|
||||
return $this->success(['person' => $person]);
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
$this->authorize('update', Person::class);
|
||||
|
||||
$person = Person::findOrFail($id);
|
||||
|
||||
$data = request()->all();
|
||||
$data['popularity'] = Arr::get($data, 'popularity') ?: 50;
|
||||
$person->fill($data)->save();
|
||||
|
||||
return $this->success(['person' => $person]);
|
||||
}
|
||||
|
||||
public function destroy(string $ids)
|
||||
{
|
||||
$ids = explode(',', $ids);
|
||||
$this->authorize('destroy', Person::class);
|
||||
|
||||
app(DeletePeople::class)->execute($ids);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
30
app/Http/Controllers/PersonCreditsController.php
Executable file
30
app/Http/Controllers/PersonCreditsController.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\People\GetPersonCredits;
|
||||
use App\Models\Person;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class PersonCreditsController extends BaseController
|
||||
{
|
||||
public function fullTitleCredits(
|
||||
Person $person,
|
||||
int $titleId,
|
||||
string $department,
|
||||
) {
|
||||
$this->authorize('show', Person::class);
|
||||
|
||||
$credits = app(GetPersonCredits::class)->execute($person, [
|
||||
'titleId' => $titleId,
|
||||
]);
|
||||
|
||||
$title = Arr::first(
|
||||
$credits['credits'][$department],
|
||||
fn($title) => $title['id'] === (int) $titleId,
|
||||
);
|
||||
|
||||
return $this->success(['credits' => $title['episodes']]);
|
||||
}
|
||||
}
|
||||
24
app/Http/Controllers/RelatedTitlesController.php
Executable file
24
app/Http/Controllers/RelatedTitlesController.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Titles\Retrieve\GetRelatedTitles;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class RelatedTitlesController extends BaseController
|
||||
{
|
||||
public function index(int $id)
|
||||
{
|
||||
$this->authorize('index', Title::class);
|
||||
|
||||
$title = Title::with('keywords', 'genres')->findOrFail($id);
|
||||
|
||||
$related = app(GetRelatedTitles::class)->execute(
|
||||
$title,
|
||||
request()->all(),
|
||||
);
|
||||
|
||||
return $this->success(['titles' => $related]);
|
||||
}
|
||||
}
|
||||
135
app/Http/Controllers/ReviewController.php
Executable file
135
app/Http/Controllers/ReviewController.php
Executable file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Reviews\UpdateReviewableAverageScore;
|
||||
use App\Models\Review;
|
||||
use Auth;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Database\Datasource\Datasource;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ReviewController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', Review::class);
|
||||
|
||||
$builder = Review::withCount('reports')->withTextOnly();
|
||||
|
||||
// will need to specify this outside of filters on edit title reviews page
|
||||
if (request('reviewable_id') && request('reviewable_type')) {
|
||||
$builder->where([
|
||||
'reviewable_id' => request('reviewable_id'),
|
||||
'reviewable_type' => request('reviewable_type'),
|
||||
]);
|
||||
}
|
||||
|
||||
$datasource = new Datasource($builder, request()->all());
|
||||
$order = $datasource->getOrder();
|
||||
|
||||
if (Str::endsWith($order['col'], 'mostHelpful')) {
|
||||
$datasource->order = false;
|
||||
$builder->orderByMostHelpful();
|
||||
}
|
||||
|
||||
$pagination = $datasource->paginate()->through(function ($review) {
|
||||
if ($review->relationLoaded('reviewable') && $review->reviewable) {
|
||||
$normalized = $review->reviewable->toNormalizedArray();
|
||||
$review->unsetRelation('reviewable');
|
||||
$review->setAttribute('reviewable', $normalized);
|
||||
}
|
||||
return $review;
|
||||
});
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
$review = Review::findOrFail($id);
|
||||
|
||||
$this->authorize('update', $review);
|
||||
|
||||
$data = request()->all();
|
||||
|
||||
if (isset($data['body'])) {
|
||||
$data['has_text'] = true;
|
||||
}
|
||||
|
||||
$review->fill($data)->save();
|
||||
|
||||
app(UpdateReviewableAverageScore::class)->execute(
|
||||
$review->reviewable_id,
|
||||
$review->reviewable_type,
|
||||
);
|
||||
|
||||
return $this->success(['review' => $review]);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
$this->authorize('store', Review::class);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'reviewable_id' => 'required|integer',
|
||||
'reviewable_type' => 'required|string',
|
||||
'title' => 'string|min:10|max:150',
|
||||
'body' => 'string|min:100|max:5000',
|
||||
'score' => 'required|integer|min:1|max:10',
|
||||
]);
|
||||
|
||||
$reviewableId = $data['reviewable_id'];
|
||||
$reviewableType = $data['reviewable_type'];
|
||||
|
||||
$values = [
|
||||
'score' => request('score'),
|
||||
];
|
||||
|
||||
// don't override review body or title when only score changes
|
||||
if (request('body')) {
|
||||
$values['body'] = request('body');
|
||||
$values['has_text'] = true;
|
||||
}
|
||||
if (request('title')) {
|
||||
$values['title'] = request('title');
|
||||
}
|
||||
|
||||
$review = Review::updateOrCreate(
|
||||
[
|
||||
'user_id' => Auth::id(),
|
||||
'reviewable_type' => $reviewableType,
|
||||
'reviewable_id' => $reviewableId,
|
||||
],
|
||||
$values,
|
||||
);
|
||||
|
||||
$review->load('user');
|
||||
app(UpdateReviewableAverageScore::class)->execute(
|
||||
$reviewableId,
|
||||
$reviewableType,
|
||||
);
|
||||
|
||||
return $this->success(['review' => $review]);
|
||||
}
|
||||
|
||||
public function destroy(string $ids)
|
||||
{
|
||||
$reviewIds = explode(',', $ids);
|
||||
|
||||
$reviews = Review::whereIn('id', $reviewIds)->get();
|
||||
|
||||
$this->authorize('destroy', [Review::class, $reviews]);
|
||||
|
||||
$reviews->each(function (Review $review) {
|
||||
app(UpdateReviewableAverageScore::class)->execute(
|
||||
$review->reviewable_id,
|
||||
$review->reviewable_type,
|
||||
);
|
||||
});
|
||||
|
||||
Review::whereIn('id', $reviews->pluck('id'))->delete();
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
44
app/Http/Controllers/ReviewFeedbackController.php
Executable file
44
app/Http/Controllers/ReviewFeedbackController.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Review;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class ReviewFeedbackController extends BaseController
|
||||
{
|
||||
public function store(Review $review)
|
||||
{
|
||||
$this->authorize('show', $review);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'is_helpful' => 'required|boolean',
|
||||
]);
|
||||
$isHelpful = $data['is_helpful'];
|
||||
|
||||
$review->feedback()->updateOrCreate(
|
||||
[
|
||||
'user_id' => auth()->id(),
|
||||
],
|
||||
[
|
||||
'is_helpful' => $isHelpful,
|
||||
],
|
||||
);
|
||||
|
||||
$review->timestamps = false;
|
||||
|
||||
if ($isHelpful) {
|
||||
$review->increment('helpful_count');
|
||||
if ($review->not_helpful_count > 0) {
|
||||
$review->decrement('not_helpful_count');
|
||||
}
|
||||
} else {
|
||||
if ($review->helpful_count > 0) {
|
||||
$review->decrement('helpful_count');
|
||||
}
|
||||
$review->increment('not_helpful_count');
|
||||
}
|
||||
|
||||
return $this->success(['review' => $review->load('feedback')]);
|
||||
}
|
||||
}
|
||||
29
app/Http/Controllers/ReviewableController.php
Executable file
29
app/Http/Controllers/ReviewableController.php
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Loaders\ReviewsLoader;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class ReviewableController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = (new ReviewsLoader())->loadData([
|
||||
'reviewableType' => request('reviewable_type'),
|
||||
'reviewableId' => request('reviewable_id'),
|
||||
'page' => request('page'),
|
||||
'orderBy' => request('orderBy'),
|
||||
'orderDir' => request('orderDir'),
|
||||
'perPage' => request('perPage'),
|
||||
]);
|
||||
|
||||
if (!$data) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$this->authorize('show', $data['reviewable']);
|
||||
|
||||
return $this->success($data);
|
||||
}
|
||||
}
|
||||
73
app/Http/Controllers/SearchController.php
Executable file
73
app/Http/Controllers/SearchController.php
Executable file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\LocalSearch;
|
||||
use App\Services\Data\Tmdb\TmdbApi;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Collection;
|
||||
use Str;
|
||||
|
||||
class SearchController extends BaseController
|
||||
{
|
||||
public function index($query)
|
||||
{
|
||||
$dataProvider =
|
||||
request('provider') ?: settings('content.search_provider');
|
||||
$results = $this->searchUsing($dataProvider, $query)
|
||||
->map(function ($result) {
|
||||
if (isset($result['description'])) {
|
||||
$result['description'] = Str::limit(
|
||||
$result['description'],
|
||||
140,
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
})
|
||||
->values();
|
||||
|
||||
$data = [
|
||||
'results' => $results,
|
||||
'query' => trim(strip_tags($query), '"\''),
|
||||
'loader' => 'searchPage',
|
||||
];
|
||||
|
||||
return $this->renderClientOrApi([
|
||||
'pageName' => 'search-page',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
private function searchUsing($provider, $query)
|
||||
{
|
||||
$params = request()->all();
|
||||
$params['limit'] =
|
||||
request('loader', 'searchPage') === 'searchPage' ? 20 : 8;
|
||||
|
||||
if ($provider === 'tmdb') {
|
||||
return app(TmdbApi::class)->search($query, $params);
|
||||
}
|
||||
|
||||
$results = app(LocalSearch::class)->execute($query, $params);
|
||||
|
||||
if ($provider === 'all') {
|
||||
$tmdb = app(TmdbApi::class)->search($query, $params);
|
||||
$results = $results
|
||||
->concat($tmdb)
|
||||
->unique(
|
||||
fn($item) => ($item['tmdb_id'] ?: $item['name']) .
|
||||
$item['model_type'],
|
||||
)
|
||||
->groupBy('model_type')
|
||||
// make sure specified limit is enforced per group
|
||||
// (title, person) instead of the whole collection
|
||||
->map(
|
||||
fn(Collection $group) => $group->slice(0, $params['limit']),
|
||||
)
|
||||
->flatten(1)
|
||||
->sortByDesc('popularity');
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
59
app/Http/Controllers/SeasonController.php
Executable file
59
app/Http/Controllers/SeasonController.php
Executable file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Titles\DeleteSeasons;
|
||||
use App\Actions\Titles\LoadSeasonEpisodeNumbers;
|
||||
use App\Loaders\SeasonLoader;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class SeasonController extends BaseController
|
||||
{
|
||||
public function show()
|
||||
{
|
||||
$data = (new SeasonLoader())->loadData(request('loader'));
|
||||
|
||||
$this->authorize('show', $data['title']);
|
||||
|
||||
return $this->renderClientOrApi([
|
||||
'pageName' => 'season-page',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store($titleId)
|
||||
{
|
||||
$this->authorize('update', Title::class);
|
||||
|
||||
$title = Title::withCount('seasons')->findOrFail($titleId);
|
||||
|
||||
$season = $title->seasons()->create([
|
||||
'number' => $title->seasons_count + 1,
|
||||
]);
|
||||
|
||||
return $this->success(['season' => $season]);
|
||||
}
|
||||
|
||||
public function destroy(int $seasonId)
|
||||
{
|
||||
$this->authorize('update', Title::class);
|
||||
|
||||
app(DeleteSeasons::class)->execute([$seasonId]);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function episodeNumbers()
|
||||
{
|
||||
$titleId = request()->route('titleId');
|
||||
$seasonNumber = request()->route('seasonNumber');
|
||||
|
||||
$episodeNumbers = (new LoadSeasonEpisodeNumbers())->execute(
|
||||
$titleId,
|
||||
$seasonNumber,
|
||||
);
|
||||
|
||||
return $this->success(['episodeNumbers' => $episodeNumbers]);
|
||||
}
|
||||
}
|
||||
23
app/Http/Controllers/SeasonEpisodesController.php
Executable file
23
app/Http/Controllers/SeasonEpisodesController.php
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Titles\Retrieve\PaginateSeasonEpisodes;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class SeasonEpisodesController extends BaseController
|
||||
{
|
||||
public function __invoke(Title $title, int $seasonNumber)
|
||||
{
|
||||
$this->authorize('show', $title);
|
||||
|
||||
$pagination = app(PaginateSeasonEpisodes::class)->execute(
|
||||
$title,
|
||||
$seasonNumber,
|
||||
request()->all(),
|
||||
);
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
}
|
||||
61
app/Http/Controllers/TitleAutocompleteController.php
Executable file
61
app/Http/Controllers/TitleAutocompleteController.php
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Titles\LoadSeasonEpisodeNumbers;
|
||||
use App\Models\Season;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class TitleAutocompleteController extends BaseController
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
$this->authorize('index', Title::class);
|
||||
|
||||
$search = request('searchQuery');
|
||||
$selectedTitleId = request('selectedTitleId');
|
||||
$seasonNumber = request('seasonNumber');
|
||||
$builder = app(Title::class);
|
||||
|
||||
if ($search) {
|
||||
$builder = $builder->search($search);
|
||||
}
|
||||
|
||||
$results = $builder
|
||||
->take(10)
|
||||
->get(['id', 'name', 'poster', 'release_date']);
|
||||
|
||||
$results = $results->map(function (Title $title) {
|
||||
$normalized = $title->toNormalizedArray();
|
||||
if ($title->relationLoaded('season')) {
|
||||
$normalized['episodes_count'] =
|
||||
$title->season->episodes_count ?? 0;
|
||||
}
|
||||
$normalized['seasons_count'] = $title->seasons_count;
|
||||
return $normalized;
|
||||
});
|
||||
|
||||
if ($selectedTitleId) {
|
||||
$title = Title::find($selectedTitleId);
|
||||
if ($title) {
|
||||
$normalizedTitle = $title->toNormalizedArray();
|
||||
$normalizedTitle['seasons_count'] = Season::where(
|
||||
'title_id',
|
||||
$title->id,
|
||||
)->count();
|
||||
if ($seasonNumber) {
|
||||
$normalizedTitle[
|
||||
'episode_numbers'
|
||||
] = (new LoadSeasonEpisodeNumbers())->execute(
|
||||
$title->id,
|
||||
$seasonNumber,
|
||||
);
|
||||
}
|
||||
$results->prepend($normalizedTitle);
|
||||
}
|
||||
}
|
||||
|
||||
return ['titles' => $results];
|
||||
}
|
||||
}
|
||||
70
app/Http/Controllers/TitleController.php
Executable file
70
app/Http/Controllers/TitleController.php
Executable file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Titles\DeleteTitles;
|
||||
use App\Actions\Titles\Retrieve\PaginateTitles;
|
||||
use App\Actions\Titles\Store\StoreTitleData;
|
||||
use App\Jobs\IncrementModelViews;
|
||||
use App\Loaders\TitleLoader;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class TitleController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', Title::class);
|
||||
|
||||
$pagination = app(PaginateTitles::class)->execute(request()->all());
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function show()
|
||||
{
|
||||
$data = (new TitleLoader())->loadData(request('loader'));
|
||||
|
||||
$this->authorize('show', $data['title']);
|
||||
|
||||
(new IncrementModelViews())->execute($data['title']);
|
||||
|
||||
return $this->renderClientOrApi([
|
||||
'pageName' => 'title-page',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(int $id)
|
||||
{
|
||||
$this->authorize('update', Title::class);
|
||||
|
||||
$data = request()->all();
|
||||
$title = Title::findOrFail($id);
|
||||
|
||||
$title = app(StoreTitleData::class)->execute($title, $data, [
|
||||
'overrideWithEmptyValues' => true,
|
||||
]);
|
||||
|
||||
return $this->success(['title' => $title]);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
$this->authorize('store', Title::class);
|
||||
|
||||
$title = Title::create(request()->all());
|
||||
|
||||
return $this->success(['title' => $title]);
|
||||
}
|
||||
|
||||
public function destroy(string $ids)
|
||||
{
|
||||
$titleIds = explode(',', $ids);
|
||||
$this->authorize('destroy', Title::class);
|
||||
|
||||
app(DeleteTitles::class)->execute($titleIds);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
115
app/Http/Controllers/TitleCreditsController.php
Executable file
115
app/Http/Controllers/TitleCreditsController.php
Executable file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Episode;
|
||||
use App\Models\Season;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Database\Datasource\Datasource;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TitleCreditsController extends BaseController
|
||||
{
|
||||
public function index(Title $title)
|
||||
{
|
||||
$this->authorize('show', $title);
|
||||
|
||||
$model = $this->resolveCreditableModel($title, request()->all());
|
||||
|
||||
$builder = $model->credits();
|
||||
|
||||
if (request('crewOnly')) {
|
||||
$builder->wherePivot('department', '!=', 'actors');
|
||||
}
|
||||
|
||||
if ($department = request('department')) {
|
||||
$builder->wherePivot('department', $department);
|
||||
}
|
||||
|
||||
$datasource = new Datasource($builder, request()->all());
|
||||
$datasource->order = false;
|
||||
|
||||
return $this->success(['pagination' => $datasource->paginate()]);
|
||||
}
|
||||
|
||||
public function update(Title $title, int $pivotId)
|
||||
{
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'character' => 'string|nullable',
|
||||
'department' => 'string|nullable',
|
||||
'job' => 'string|nullable',
|
||||
]);
|
||||
|
||||
$model = $this->resolveCreditableModel($title, request()->all());
|
||||
$model->updateCredit($pivotId, $data);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function store(Title $title)
|
||||
{
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'person_id' => 'required|integer|exists:people,id',
|
||||
'character' => 'required_if:department,cast|string',
|
||||
'department' => 'required|string',
|
||||
'job' => 'string|nullable',
|
||||
]);
|
||||
|
||||
$model = $this->resolveCreditableModel($title, request()->all());
|
||||
$model->createCredit($data);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function destroy(Title $title, int $pivotId)
|
||||
{
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$model = $this->resolveCreditableModel($title, request()->all());
|
||||
$model
|
||||
->credits()
|
||||
->wherePivot('id', $pivotId)
|
||||
->detach();
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function changeOrder()
|
||||
{
|
||||
$this->authorize('update', Title::class);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'ids' => 'array|min:1',
|
||||
'ids.*' => 'integer',
|
||||
]);
|
||||
|
||||
$queryPart = '';
|
||||
foreach ($data['ids'] as $order => $id) {
|
||||
$queryPart .= " when id=$id then $order";
|
||||
}
|
||||
|
||||
DB::table('creditables')
|
||||
->whereIn('id', $data['ids'])
|
||||
->update(['order' => DB::raw("(case $queryPart end)")]);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function resolveCreditableModel(
|
||||
Title $title,
|
||||
array $params,
|
||||
): Title|Season|Episode {
|
||||
if (Arr::get($params, 'season') && Arr::get($params, 'episode')) {
|
||||
return $title->findEpisode($params['season'], $params['episode']);
|
||||
} elseif (Arr::get($params, 'season')) {
|
||||
return $title->findSeason($params['season']);
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
20
app/Http/Controllers/TitleNewsController.php
Executable file
20
app/Http/Controllers/TitleNewsController.php
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class TitleNewsController extends BaseController
|
||||
{
|
||||
public function __invoke(Title $title)
|
||||
{
|
||||
$this->authorize('show', $title);
|
||||
|
||||
$articles = $title->load([
|
||||
'newsArticles' => fn($q) => $q->limit(4),
|
||||
])->newsArticles;
|
||||
|
||||
return $this->success(['news_articles' => $articles]);
|
||||
}
|
||||
}
|
||||
22
app/Http/Controllers/TitleSeasonsController.php
Executable file
22
app/Http/Controllers/TitleSeasonsController.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Titles\Retrieve\PaginateTitleSeasons;
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class TitleSeasonsController extends BaseController
|
||||
{
|
||||
public function __invoke(Title $title)
|
||||
{
|
||||
$this->authorize('show', $title);
|
||||
|
||||
$pagination = app(PaginateTitleSeasons::class)->execute(
|
||||
$title,
|
||||
request()->all(),
|
||||
);
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
}
|
||||
46
app/Http/Controllers/TitleTagsController.php
Executable file
46
app/Http/Controllers/TitleTagsController.php
Executable file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TitleTagsController extends BaseController
|
||||
{
|
||||
public function store(Title $title, string $type)
|
||||
{
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'tag_name' => 'required|string',
|
||||
]);
|
||||
|
||||
$relation = $this->getRelationName($type);
|
||||
|
||||
$tags = $title
|
||||
->$relation()
|
||||
->getModel()
|
||||
->insertOrRetrieve([$data['tag_name']]);
|
||||
|
||||
$title->$relation()->syncWithoutDetaching($tags->pluck('id'));
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function destroy(Title $title, string $type, int $tagId)
|
||||
{
|
||||
$this->authorize('update', $title);
|
||||
|
||||
$relation = $this->getRelationName($type);
|
||||
|
||||
$title->$relation()->detach([$tagId]);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
private function getRelationName($type)
|
||||
{
|
||||
return Str::plural(Str::camel($type));
|
||||
}
|
||||
}
|
||||
147
app/Http/Controllers/UserProfileController.php
Executable file
147
app/Http/Controllers/UserProfileController.php
Executable file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Lists\ListsLoader;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Title;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Common\Auth\Events\UserAvatarChanged;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Database\Datasource\Datasource;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
class UserProfileController extends BaseController
|
||||
{
|
||||
public function show(User $user)
|
||||
{
|
||||
$this->authorize('show', $user);
|
||||
|
||||
$user->load(['profile', 'links']);
|
||||
$user->loadCount(['followers', 'followedUsers', 'lists']);
|
||||
|
||||
$user->is_pro = $user->subscribed();
|
||||
|
||||
return $this->success(['user' => $user]);
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$this->authorize('update', $user);
|
||||
|
||||
$data = $this->validate(request(), [
|
||||
'user' => 'array',
|
||||
'profile' => 'array',
|
||||
'links' => 'array',
|
||||
]);
|
||||
|
||||
User::unguard(true);
|
||||
$oldAvatar = $user->avatar;
|
||||
$user->fill($data['user'])->save();
|
||||
|
||||
if (
|
||||
isset($data['user']['avatar']) &&
|
||||
$oldAvatar !== $data['user']['avatar']
|
||||
) {
|
||||
event(new UserAvatarChanged($user));
|
||||
}
|
||||
|
||||
$profile = $user
|
||||
->profile()
|
||||
->updateOrCreate(['user_id' => $user->id], $data['profile']);
|
||||
|
||||
$user->links()->delete();
|
||||
$links = $user->links()->createMany($data['links']);
|
||||
|
||||
$user->setRelation('profile', $profile);
|
||||
$user->setRelation('links', $links);
|
||||
|
||||
return $this->success(['user' => $user]);
|
||||
}
|
||||
|
||||
public function lists(User $user)
|
||||
{
|
||||
$this->authorize('show', $user);
|
||||
|
||||
$pagination = (new ListsLoader())->forUser($user, request()->all());
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function ratings(User $user)
|
||||
{
|
||||
$this->authorize('show', $user);
|
||||
|
||||
$datasource = new Datasource(
|
||||
$user
|
||||
->reviews()
|
||||
->whereNull('body')
|
||||
->with([
|
||||
'reviewable' => function (MorphTo $morphTo) {
|
||||
$morphTo
|
||||
->morphWith([
|
||||
Episode::class => ['title'],
|
||||
])
|
||||
->with('primaryVideo');
|
||||
},
|
||||
'user',
|
||||
]),
|
||||
request()->all(),
|
||||
);
|
||||
|
||||
$pagination = $datasource->paginate();
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function reviews(User $user)
|
||||
{
|
||||
$this->authorize('show', $user);
|
||||
|
||||
$datasource = new Datasource(
|
||||
$user
|
||||
->reviews()
|
||||
->where('reviewable_type', Title::MODEL_TYPE)
|
||||
->whereNotNull('body')
|
||||
->with([
|
||||
'reviewable' => function (MorphTo $morphTo) {
|
||||
$morphTo->morphWith([
|
||||
Episode::class => ['title'],
|
||||
]);
|
||||
},
|
||||
'user',
|
||||
]),
|
||||
request()->all(),
|
||||
);
|
||||
|
||||
$pagination = $datasource->paginate();
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function comments(User $user)
|
||||
{
|
||||
$this->authorize('show', $user);
|
||||
|
||||
$datasource = new Datasource(
|
||||
$user
|
||||
->comments()
|
||||
->with([
|
||||
'commentable' => function (MorphTo $morphTo) {
|
||||
$morphTo->morphWith([
|
||||
Episode::class => ['title'],
|
||||
]);
|
||||
},
|
||||
'user',
|
||||
])
|
||||
->where('deleted', false),
|
||||
request()->all(),
|
||||
);
|
||||
|
||||
$pagination = $datasource->paginate();
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
}
|
||||
46
app/Http/Controllers/UserRatingsController.php
Executable file
46
app/Http/Controllers/UserRatingsController.php
Executable file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UserRatingsController extends BaseController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function __invoke()
|
||||
{
|
||||
$ratings = Auth::user()
|
||||
->reviews()
|
||||
->select(['id', 'reviewable_id', 'reviewable_type', 'score'])
|
||||
->limit(1000)
|
||||
->get()
|
||||
->map(
|
||||
fn($review) => [
|
||||
'id' => $review->id,
|
||||
'score' => $review->score,
|
||||
'reviewable_id' => $review->reviewable_id,
|
||||
'type' => $review->reviewable_type,
|
||||
],
|
||||
)
|
||||
->groupBy('type')
|
||||
->map(
|
||||
fn($group) => $group
|
||||
->mapWithKeys(
|
||||
fn($item) => [
|
||||
$item['reviewable_id'] => [
|
||||
'id' => $item['id'],
|
||||
'score' => $item['score'],
|
||||
],
|
||||
],
|
||||
)
|
||||
->all(),
|
||||
);
|
||||
|
||||
return $this->success(['ratings' => $ratings]);
|
||||
}
|
||||
}
|
||||
47
app/Http/Controllers/UserWatchlistController.php
Executable file
47
app/Http/Controllers/UserWatchlistController.php
Executable file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UserWatchlistController extends BaseController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function __invoke()
|
||||
{
|
||||
$list = Auth::user()
|
||||
->watchlist()
|
||||
->firstOrFail();
|
||||
|
||||
$items = DB::table('channelables')
|
||||
->where('channel_id', $list->id)
|
||||
->pluck('channelable_type', 'channelable_id')
|
||||
->map(
|
||||
fn($modelType, $itemId) => [
|
||||
'id' => $itemId,
|
||||
'type' => $modelType,
|
||||
],
|
||||
)
|
||||
->groupBy('type')
|
||||
->map(
|
||||
fn($group) => $group->mapWithKeys(
|
||||
fn($item) => [
|
||||
$item['id'] => true,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return $this->success([
|
||||
'watchlist' => [
|
||||
'id' => $list->id,
|
||||
'items' => $items,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
27
app/Http/Controllers/VideoApproveController.php
Executable file
27
app/Http/Controllers/VideoApproveController.php
Executable file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Video;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class VideoApproveController extends BaseController
|
||||
{
|
||||
public function approve(Video $video)
|
||||
{
|
||||
$this->authorize('update', $video);
|
||||
|
||||
$video->update(['approved' => true]);
|
||||
|
||||
return $this->success(['video' => $video]);
|
||||
}
|
||||
|
||||
public function disapprove(Video $video)
|
||||
{
|
||||
$this->authorize('update', $video);
|
||||
|
||||
$video->update(['approved' => false]);
|
||||
|
||||
return $this->success(['video' => $video]);
|
||||
}
|
||||
}
|
||||
33
app/Http/Controllers/VideoOrderController.php
Executable file
33
app/Http/Controllers/VideoOrderController.php
Executable file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Title;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class VideoOrderController extends BaseController
|
||||
{
|
||||
public function changeOrder(int $titleId)
|
||||
{
|
||||
$title = Title::findOrFail($titleId);
|
||||
|
||||
$this->authorize('update', $title);
|
||||
|
||||
request()->validate([
|
||||
'ids' => 'array|min:1',
|
||||
'ids.*' => 'integer',
|
||||
]);
|
||||
|
||||
$queryPart = '';
|
||||
foreach (request('ids') as $order => $id) {
|
||||
$queryPart .= " when id=$id then $order";
|
||||
}
|
||||
|
||||
DB::table('videos')
|
||||
->whereIn('id', request('ids'))
|
||||
->update(['order' => DB::raw("(case $queryPart end)")]);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
35
app/Http/Controllers/VideoReportController.php
Executable file
35
app/Http/Controllers/VideoReportController.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Video;
|
||||
use Auth;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class VideoReportController extends BaseController
|
||||
{
|
||||
public function report(Video $video)
|
||||
{
|
||||
$userId = Auth::id();
|
||||
$userIp = $this->request->ip();
|
||||
|
||||
// if we can't match current user, bail
|
||||
if ( ! $userId && ! $userIp) return null;
|
||||
|
||||
$alreadyReported = $video->reports()
|
||||
->where(function(Builder $query) use($userId, $userIp) {
|
||||
$query->where('user_id', $userId)->orWhere('user_ip', $userIp);
|
||||
})->first();
|
||||
|
||||
if ($alreadyReported) {
|
||||
return $this->error(__('You have already reported this video.'));
|
||||
} else {
|
||||
$report = $video->reports()->create([
|
||||
'user_id' => $userId,
|
||||
'user_ip' => $userIp
|
||||
]);
|
||||
return $this->success(['report' => $report]);
|
||||
}
|
||||
}
|
||||
}
|
||||
172
app/Http/Controllers/VideosController.php
Executable file
172
app/Http/Controllers/VideosController.php
Executable file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Plays\LogVideoPlay;
|
||||
use App\Actions\Videos\CrupdateVideo;
|
||||
use App\Models\Video;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Database\Datasource\Datasource;
|
||||
use Common\Database\Datasource\DatasourceFilters;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class VideosController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', Video::class);
|
||||
|
||||
$builder = Video::with([
|
||||
'captions',
|
||||
'title' => fn(BelongsTo $query) => $query->with('seasons'),
|
||||
])->withCount(['reports', 'plays']);
|
||||
|
||||
$filters = new DatasourceFilters(request('filters'));
|
||||
|
||||
if (
|
||||
$titleFilter =
|
||||
$filters->getAndRemove('title_id') ?? request('title_id')
|
||||
) {
|
||||
$titleId = $titleFilter['value'] ?? request('title_id');
|
||||
$season = $titleFilter['season'] ?? request('season');
|
||||
$episode = $titleFilter['episode'] ?? request('episode');
|
||||
$builder->where('title_id', $titleId);
|
||||
$builder->when($season, fn($q) => $q->where('season_num', $season));
|
||||
$builder->when(
|
||||
$episode,
|
||||
fn($q) => $q->where('episode_num', $episode),
|
||||
);
|
||||
}
|
||||
|
||||
$datasource = new Datasource($builder, request()->all(), $filters);
|
||||
$order = $datasource->getOrder();
|
||||
|
||||
if (Str::endsWith($order['col'], 'upvotes')) {
|
||||
$datasource->order = false;
|
||||
$builder->orderByMostUpvotes();
|
||||
}
|
||||
|
||||
// order by percentage of likes, taking into account total amount of likes and dislikes
|
||||
if (Str::endsWith($order['col'], 'score')) {
|
||||
$datasource->order = false;
|
||||
$builder->orderByWeightedScore();
|
||||
}
|
||||
|
||||
// add a secondary order by episode number if ordering by season number
|
||||
if (Str::endsWith($order['col'], 'season_num')) {
|
||||
$datasource->order = false;
|
||||
$builder
|
||||
->orderBy('season_num', 'desc')
|
||||
->orderBy('episode_num', 'desc');
|
||||
}
|
||||
|
||||
return $this->success(['pagination' => $datasource->paginate()]);
|
||||
}
|
||||
|
||||
public function show(Video $video)
|
||||
{
|
||||
$this->authorize('show', Video::class);
|
||||
|
||||
$video->load(['captions', 'title']);
|
||||
|
||||
return $this->success(['video' => $video]);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
$this->authorize('store', Video::class);
|
||||
|
||||
$this->validate(
|
||||
request(),
|
||||
[
|
||||
'title_id' => 'required|integer',
|
||||
'name' => ['required', 'string', 'min:3', 'max:250'],
|
||||
'src' => 'required|max:1000',
|
||||
'type' => 'required|string|min:3|max:250',
|
||||
'category' => 'required|string|min:3|max:20',
|
||||
'quality' => 'nullable|string|min:2|max:250',
|
||||
'language' => 'required|nullable|string|max:10',
|
||||
'season_num' => 'nullable|integer',
|
||||
'episode_num' => 'requiredWith:season|integer|nullable',
|
||||
'captions' => 'nullable|array',
|
||||
'captions.*.name' => 'required|string|max:100',
|
||||
'captions.*.url' => 'required|string|max:250',
|
||||
'captions.*.language' => 'required|string|max:100',
|
||||
],
|
||||
[
|
||||
'title_id.*' => __(
|
||||
'Select a title this video should be attached to.',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
$video = app(CrupdateVideo::class)->execute(request()->all());
|
||||
|
||||
return $this->success(['video' => $video]);
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
$this->authorize('update', Video::class);
|
||||
|
||||
$this->validate(
|
||||
request(),
|
||||
[
|
||||
'name' => 'string|min:3|max:250',
|
||||
'src' => 'required|max:1000',
|
||||
'type' => 'string|min:3|max:1000',
|
||||
'quality' => 'nullable|string|min:2|max:250',
|
||||
'language' => 'required|nullable|string|max:10',
|
||||
'title_id' => 'integer',
|
||||
'season_num' => 'nullable|integer',
|
||||
'episode_num' => 'requiredWith:season|integer|nullable',
|
||||
'captions' => 'nullable|array',
|
||||
'captions.*.name' => 'required|string|max:100',
|
||||
'captions.*.url' => 'required|string|max:250',
|
||||
'captions.*.language' => 'required|string|max:100',
|
||||
],
|
||||
[
|
||||
'title_id.*' => __(
|
||||
'Select a title this video should be attached to.',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
$video = app(CrupdateVideo::class)->execute(request()->all(), $id);
|
||||
|
||||
return $this->success(['video' => $video]);
|
||||
}
|
||||
|
||||
public function destroy($ids)
|
||||
{
|
||||
$ids = explode(',', $ids);
|
||||
$this->authorize('destroy', [Video::class, $ids]);
|
||||
|
||||
foreach ($ids as $id) {
|
||||
$video = Video::find($id);
|
||||
if (is_null($video)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$video->delete();
|
||||
}
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function logPlay(Video $video)
|
||||
{
|
||||
$this->authorize('show', Video::class);
|
||||
|
||||
if (request()->getContentType() === 'application/json') {
|
||||
$data = request()->all();
|
||||
} else {
|
||||
$data = json_decode(request()->getContent(), true);
|
||||
}
|
||||
|
||||
app(LogVideoPlay::class)->execute($video, $data);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
197
app/Http/Controllers/WatchController.php
Executable file
197
app/Http/Controllers/WatchController.php
Executable file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Plays\LogVideoPlay;
|
||||
use App\Actions\Titles\Retrieve\GetRelatedTitles;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Title;
|
||||
use App\Models\Video;
|
||||
use Common\Core\BaseController;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class WatchController extends BaseController
|
||||
{
|
||||
public function __invoke(Video $video)
|
||||
{
|
||||
$this->authorize('show', $video);
|
||||
|
||||
$video = $this->loadVideoRelations($video);
|
||||
|
||||
(new LogVideoPlay())->execute($video);
|
||||
|
||||
$title = $this->loadTitle($video);
|
||||
|
||||
$episode = $video->episode_id
|
||||
? Episode::findOrFail($video->episode_id)
|
||||
: null;
|
||||
|
||||
$data = [
|
||||
'loader' => 'watchPage',
|
||||
'title' => $title,
|
||||
'episode' => $episode,
|
||||
'video' => $video,
|
||||
'related_videos' => $this->videosByTags($video->title),
|
||||
'alternative_videos' => settings('streaming.show_video_selector')
|
||||
? $this->loadAlternativeVideos($video)
|
||||
: [],
|
||||
];
|
||||
|
||||
return $this->renderClientOrApi([
|
||||
'data' => $data,
|
||||
'pageName' => 'watch-page',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function loadTitle(Video $video)
|
||||
{
|
||||
$title = Title::select([
|
||||
'id',
|
||||
'name',
|
||||
'description',
|
||||
'backdrop',
|
||||
'poster',
|
||||
'is_series',
|
||||
])
|
||||
->withCount('seasons')
|
||||
->findOrFail($video->title_id);
|
||||
|
||||
$title->description = Str::limit($title->description, 310);
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
protected function loadVideoRelations(Video $video)
|
||||
{
|
||||
$video->load([
|
||||
'captions',
|
||||
'reports' => fn($q) => $q
|
||||
->where('user_id', auth()->id())
|
||||
->orWhere('user_ip', getIp()),
|
||||
'latestPlay' => function (HasOne $builder) {
|
||||
$builder
|
||||
->forCurrentUser()
|
||||
->whereNotNull('time_watched')
|
||||
->select(['id', 'video_id', 'time_watched']);
|
||||
},
|
||||
]);
|
||||
if ($video->reports->first()) {
|
||||
$video->current_user_reported = true;
|
||||
}
|
||||
return $video;
|
||||
}
|
||||
|
||||
private function videosByEpisode(Title $title, int $seasonNum)
|
||||
{
|
||||
[$col, $direction] = explode(':', settings('streaming.default_sort'));
|
||||
|
||||
$currSeasonNum = $seasonNum;
|
||||
$prevSeasonNum = $seasonNum - 1;
|
||||
$nextSeasonNum = $seasonNum + 1;
|
||||
|
||||
$videos = $title
|
||||
->videos()
|
||||
->with(['captions', 'episode', 'title' => fn($q) => $q->compact()])
|
||||
->where('approved', true)
|
||||
->where('category', 'full')
|
||||
->whereNotNull('episode_id')
|
||||
->whereIn('season_num', [
|
||||
$currSeasonNum,
|
||||
$nextSeasonNum,
|
||||
$prevSeasonNum,
|
||||
])
|
||||
->orderBy($col, $direction)
|
||||
->groupBy(['season_num', 'episode_num'])
|
||||
->get();
|
||||
|
||||
if ($videos->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$grouped = $videos
|
||||
->groupBy('season_num')
|
||||
->map(function (Collection $videos, $seasonNum) use (
|
||||
$prevSeasonNum,
|
||||
$nextSeasonNum,
|
||||
) {
|
||||
if ($seasonNum === $prevSeasonNum) {
|
||||
return $videos->sortByDesc('episode_num')->first();
|
||||
} elseif ($seasonNum === $nextSeasonNum) {
|
||||
return $videos->sortByDesc('episode_num')->last();
|
||||
} else {
|
||||
// current season episodes
|
||||
return $videos->sortBy('season')->sortBy('episode_num');
|
||||
}
|
||||
});
|
||||
|
||||
$videos = $grouped->get($currSeasonNum)->values();
|
||||
|
||||
// make sure prev season last episode appears first
|
||||
if ($prevSeason = $grouped->get($prevSeasonNum)) {
|
||||
$videos->prepend($prevSeason);
|
||||
}
|
||||
|
||||
// make sure next season first episode appears last
|
||||
if ($nextSeason = $grouped->get($nextSeasonNum)) {
|
||||
$videos->push($nextSeason);
|
||||
}
|
||||
|
||||
return $videos->values();
|
||||
}
|
||||
|
||||
private function videosByTags(Title $title)
|
||||
{
|
||||
$title->load(['keywords', 'genres']);
|
||||
|
||||
$related = app(GetRelatedTitles::class)->execute($title, [
|
||||
'limit' => 6,
|
||||
'compact' => true,
|
||||
]);
|
||||
|
||||
$videos = [];
|
||||
|
||||
if ($related->isNotEmpty()) {
|
||||
$related->load([
|
||||
'videos' => fn(HasMany $builder) => $builder
|
||||
->where('approved', true)
|
||||
->fromConfiguredCategory(),
|
||||
]);
|
||||
$videos = $related
|
||||
->map(function (Title $title) {
|
||||
if ($video = $title->videos->first()) {
|
||||
$title->setRelation('videos', []);
|
||||
$video->title = $title;
|
||||
return $video;
|
||||
}
|
||||
})
|
||||
->filter()
|
||||
->values();
|
||||
}
|
||||
|
||||
return $videos;
|
||||
}
|
||||
|
||||
protected function loadAlternativeVideos(Video $video)
|
||||
{
|
||||
$builder = Video::where('title_id', $video->title_id);
|
||||
if ($video->season_num) {
|
||||
$builder->where('season_num', $video->season_num);
|
||||
}
|
||||
if ($video->episode_num) {
|
||||
$builder->where('episode_num', $video->episode_num);
|
||||
}
|
||||
|
||||
return $builder
|
||||
->where('approved', true)
|
||||
->limit(10)
|
||||
->when(
|
||||
settings('streaming.prefer_full'),
|
||||
fn($query) => $query->where('category', 'full'),
|
||||
)
|
||||
->applySelectedSort()
|
||||
->get();
|
||||
}
|
||||
}
|
||||
75
app/Http/Kernel.php
Executable file
75
app/Http/Kernel.php
Executable file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\EncryptCookies;
|
||||
use App\Http\Middleware\RedirectIfAuthenticated;
|
||||
use App\Http\Middleware\TrimStrings;
|
||||
use App\Http\Middleware\TrustHosts;
|
||||
use App\Http\Middleware\VerifyCsrfToken;
|
||||
use Common\Auth\Middleware\OptionalAuthenticate;
|
||||
use Common\Core\Middleware\EnsureFrontendRequestsAreStateful;
|
||||
use Common\Core\Middleware\PrerenderIfCrawler;
|
||||
use Common\Core\Middleware\TrustProxies;
|
||||
use Illuminate\Auth\Middleware\Authenticate;
|
||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
||||
use Illuminate\Auth\Middleware\Authorize;
|
||||
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
|
||||
use Illuminate\Auth\Middleware\RequirePassword;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
|
||||
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||
use Illuminate\Http\Middleware\HandleCors;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Routing\Middleware\ValidateSignature;
|
||||
use Illuminate\Session\Middleware\AuthenticateSession;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
protected $middleware = [
|
||||
TrustHosts::class,
|
||||
TrustProxies::class,
|
||||
HandleCors::class,
|
||||
CheckForMaintenanceMode::class,
|
||||
ValidatePostSize::class,
|
||||
TrimStrings::class,
|
||||
ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
AuthenticateSession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
EnsureFrontendRequestsAreStateful::class,
|
||||
'throttle:1500,1',
|
||||
SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
||||
protected $routeMiddleware = [
|
||||
'auth' => Authenticate::class,
|
||||
'auth.basic' => AuthenticateWithBasicAuth::class,
|
||||
'bindings' => SubstituteBindings::class,
|
||||
'can' => Authorize::class,
|
||||
'guest' => RedirectIfAuthenticated::class,
|
||||
'password.confirm' => RequirePassword::class,
|
||||
'signed' => ValidateSignature::class,
|
||||
'throttle' => ThrottleRequests::class,
|
||||
'verified' => EnsureEmailIsVerified::class,
|
||||
'prerenderIfCrawler' => PrerenderIfCrawler::class,
|
||||
'optionalAuth' => OptionalAuthenticate::class,
|
||||
];
|
||||
}
|
||||
16
app/Http/Middleware/EncryptCookies.php
Executable file
16
app/Http/Middleware/EncryptCookies.php
Executable file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;
|
||||
|
||||
class EncryptCookies extends BaseEncrypter
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
];
|
||||
}
|
||||
25
app/Http/Middleware/RedirectIfAuthenticated.php
Executable file
25
app/Http/Middleware/RedirectIfAuthenticated.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string|null $guard
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
{
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return response()->json(['status' => 'error', 'message' => 'already logged in'], 403);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
18
app/Http/Middleware/TrimStrings.php
Executable file
18
app/Http/Middleware/TrimStrings.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as BaseTrimmer;
|
||||
|
||||
class TrimStrings extends BaseTrimmer
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
10
app/Http/Middleware/TrustHosts.php
Executable file
10
app/Http/Middleware/TrustHosts.php
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Common\Core\BaseTrustHosts;
|
||||
|
||||
class TrustHosts extends BaseTrustHosts
|
||||
{
|
||||
//
|
||||
}
|
||||
18
app/Http/Middleware/VerifyCsrfToken.php
Executable file
18
app/Http/Middleware/VerifyCsrfToken.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Common\Core\BaseVerifyCsrfToken;
|
||||
|
||||
class VerifyCsrfToken extends BaseVerifyCsrfToken
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'secure/update/run',
|
||||
'api/v1/videos/*/log-play'
|
||||
];
|
||||
}
|
||||
38
app/Http/Requests/CrupdateCaptionRequest.php
Executable file
38
app/Http/Requests/CrupdateCaptionRequest.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Auth;
|
||||
use Common\Core\BaseFormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CrupdateCaptionRequest extends BaseFormRequest
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$required = $this->getMethod() === 'POST' ? 'required' : '';
|
||||
$ignore = $this->getMethod() === 'PUT' ? $this->route('caption')->id : '';
|
||||
$userId = $this->route('caption') ? $this->route('caption')->user_id : Auth::id();
|
||||
|
||||
return [
|
||||
'name' => [
|
||||
$required, 'string', 'min:2',
|
||||
Rule::unique('video_captions')->where('video_id', $userId)->ignore($ignore)
|
||||
],
|
||||
'language' => "$required|string|max:5",
|
||||
'caption_file' => "$required|file|mimes:txt",
|
||||
'video_id' => "$required|integer",
|
||||
];
|
||||
}
|
||||
}
|
||||
112
app/Http/Resources/ChannelResource.php
Executable file
112
app/Http/Resources/ChannelResource.php
Executable file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Channel;
|
||||
use App\Models\NewsArticle;
|
||||
use App\Models\Person;
|
||||
use App\Models\Title;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ChannelResource extends JsonResource
|
||||
{
|
||||
public function toArray($request)
|
||||
{
|
||||
$config = Arr::except($this->config, [
|
||||
'seoTitle',
|
||||
'seoDescription',
|
||||
'adminDescription',
|
||||
'presetId',
|
||||
]);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'public' => $this->public,
|
||||
'description' => $this->description,
|
||||
'config' => $config,
|
||||
'model_type' => $this->model_type,
|
||||
'restriction' => $this->restriction?->toArray(),
|
||||
'type' => $this->type,
|
||||
'content' => [
|
||||
'current_page' => $this->content->currentPage(),
|
||||
'from' => $this->content->firstItem(),
|
||||
//'last_page' => $this->content->lastPage(),
|
||||
'next_page' => $this->content->hasMorePages()
|
||||
? $this->content->currentPage() + 1
|
||||
: null,
|
||||
'per_page' => $this->content->perPage(),
|
||||
'prev_page' =>
|
||||
$this->content->currentPage() > 1
|
||||
? $this->content->currentPage() - 1
|
||||
: null,
|
||||
'to' => $this->content->lastItem(),
|
||||
'total' =>
|
||||
$this->content instanceof LengthAwarePaginator
|
||||
? $this->content->total()
|
||||
: null,
|
||||
'data' => $this->content
|
||||
->getCollection()
|
||||
->map(function ($item) use ($request) {
|
||||
return match ($item->model_type) {
|
||||
Channel::MODEL_TYPE => (new ChannelResource(
|
||||
$item,
|
||||
))->toArray($request),
|
||||
Title::MODEL_TYPE => [
|
||||
'id' => $item->id,
|
||||
'name' => $item->name,
|
||||
'release_date' => $item->release_date,
|
||||
'poster' => $item->poster,
|
||||
'backdrop' => $item->backdrop,
|
||||
'is_series' => $item->is_series,
|
||||
'rating' => $item->rating,
|
||||
'runtime' => $item->runtime,
|
||||
'model_type' => $item::MODEL_TYPE,
|
||||
'status' => $item->status,
|
||||
'certification' => $item->certification,
|
||||
'description' => Str::limit(
|
||||
$item->description,
|
||||
200,
|
||||
),
|
||||
'primary_video' => $item->relationLoaded(
|
||||
'primaryVideo',
|
||||
)
|
||||
? $item->primaryVideo?->toArray()
|
||||
: null,
|
||||
],
|
||||
Person::MODEL_TYPE => [
|
||||
'id' => $item->id,
|
||||
'name' => $item->name,
|
||||
'poster' => $item->poster,
|
||||
'primary_credit' => $item->primary_credit,
|
||||
'known_for' => $item->known_for,
|
||||
'birth_date' => $item->birth_date,
|
||||
'death_date' => $item->death_date,
|
||||
'model_type' => $item::MODEL_TYPE,
|
||||
'description' => Str::limit(
|
||||
$item->description,
|
||||
200,
|
||||
),
|
||||
],
|
||||
NewsArticle::MODEL_TYPE => [
|
||||
'id' => $item->id,
|
||||
'title' => $item->title,
|
||||
'slug' => $item->slug,
|
||||
'image' => $item->image,
|
||||
'source' => $item->source,
|
||||
'source_url' => $item->source_url,
|
||||
'byline' => $item->byline,
|
||||
'model_type' => $item::MODEL_TYPE,
|
||||
'created_at' => $item->created_at,
|
||||
'body' => Str::limit($item->body, 340),
|
||||
],
|
||||
};
|
||||
}),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user