mirror of
https://github.com/devfake/flox.git
synced 2024-11-15 14:42:31 +01:00
74f733710e
* update translations * split helper file * implement refresh informations * add tests * update production files * move refresh all to a seperate request * update production files
361 lines
9.7 KiB
PHP
361 lines
9.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Models;
|
|
|
|
use App\Item as Model;
|
|
use App\Item;
|
|
use App\Services\IMDB;
|
|
use App\Services\Storage;
|
|
use App\Services\TMDB;
|
|
use GuzzleHttp\Client;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class ItemService {
|
|
|
|
private $model;
|
|
private $tmdb;
|
|
private $storage;
|
|
private $alternativeTitleService;
|
|
private $episodeService;
|
|
private $imdb;
|
|
|
|
/**
|
|
* @param Model $model
|
|
* @param TMDB $tmdb
|
|
* @param Storage $storage
|
|
* @param AlternativeTitleService $alternativeTitleService
|
|
* @param EpisodeService $episodeService
|
|
* @param IMDB $imdb
|
|
*/
|
|
public function __construct(
|
|
Model $model,
|
|
TMDB $tmdb,
|
|
Storage $storage,
|
|
AlternativeTitleService $alternativeTitleService,
|
|
EpisodeService $episodeService,
|
|
IMDB $imdb
|
|
){
|
|
$this->model = $model;
|
|
$this->tmdb = $tmdb;
|
|
$this->storage = $storage;
|
|
$this->alternativeTitleService = $alternativeTitleService;
|
|
$this->episodeService = $episodeService;
|
|
$this->imdb = $imdb;
|
|
}
|
|
|
|
/**
|
|
* @param $data
|
|
* @return Model
|
|
*/
|
|
public function create($data)
|
|
{
|
|
$data = $this->makeDataComplete($data);
|
|
|
|
$item = $this->model->store($data);
|
|
|
|
$this->episodeService->create($item);
|
|
$this->alternativeTitleService->create($item);
|
|
|
|
$this->storage->downloadImages($item->poster, $item->backdrop);
|
|
|
|
return $item;
|
|
}
|
|
|
|
/**
|
|
* Search against TMDb and IMDb for more informations.
|
|
* We don't need to get more informations if we add the item from the subpage.
|
|
*
|
|
* @param $data
|
|
* @return array
|
|
*/
|
|
public function makeDataComplete($data)
|
|
{
|
|
if( ! isset($data['imdb_id'])) {
|
|
$details = $this->tmdb->details($data['tmdb_id'], $data['media_type']);
|
|
$title = $details->name ?? $details->title;
|
|
|
|
$data['imdb_id'] = $data['imdb_id'] ?? $this->parseImdbId($details);
|
|
$data['youtube_key'] = $data['youtube_key'] ?? $this->parseYoutubeKey($details, $data['media_type']);
|
|
$data['overview'] = $data['overview'] ?? $details->overview;
|
|
$data['tmdb_rating'] = $data['tmdb_rating'] ?? $details->vote_average;
|
|
$data['backdrop'] = $data['backdrop'] ?? $details->backdrop_path;
|
|
$data['slug'] = $data['slug'] ?? getSlug($title);
|
|
}
|
|
|
|
$data['imdb_rating'] = $this->parseImdbRating($data);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Calls the refreshAll method with a seperate request to avoid blocking flox for the user.
|
|
*
|
|
* @param Client $client
|
|
* @return int
|
|
*/
|
|
public function refreshKickstartAll(Client $client)
|
|
{
|
|
$response = $client->get(url('/api/refresh-all'));
|
|
|
|
return $response->getStatusCode();
|
|
}
|
|
|
|
/**
|
|
* Refresh informations for all items.
|
|
*/
|
|
public function refreshAll()
|
|
{
|
|
increaseTimeLimit();
|
|
|
|
$this->model->all()->each(function($item) {
|
|
$this->refresh($item->id);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Refresh informations for an item.
|
|
* Like ratings, new episodes, new poster and backdrop images.
|
|
*
|
|
* @param $itemId
|
|
* @return Response
|
|
*/
|
|
public function refresh($itemId)
|
|
{
|
|
$item = $this->model->find($itemId);
|
|
|
|
if( ! $item) {
|
|
return response('Not Found', Response::HTTP_NOT_FOUND);
|
|
}
|
|
|
|
$this->storage->removeImages($item->poster, $item->backdrop);
|
|
|
|
$details = $this->tmdb->details($item->tmdb_id, $item->media_type);
|
|
|
|
$title = $details->name ?? $details->title;
|
|
$imdbId = $item->imdb_id ?? $this->parseImdbId($details);
|
|
|
|
$item->update([
|
|
'imdb_id' => $imdbId,
|
|
'youtube_key' => $this->parseYoutubeKey($details, $item->media_type),
|
|
'overview' => $details->overview,
|
|
'tmdb_rating' => $details->vote_average,
|
|
'imdb_rating' => $this->parseImdbRating(['imdb_id' => $imdbId]),
|
|
'backdrop' => $details->backdrop_path,
|
|
'poster' => $details->poster_path,
|
|
'slug' => getSlug($title),
|
|
'title' => $title,
|
|
'original_title' => $details->original_name ?? $details->original_title,
|
|
]);
|
|
|
|
$this->episodeService->create($item);
|
|
$this->alternativeTitleService->create($item);
|
|
|
|
$this->storage->downloadImages($item->poster, $item->backdrop);
|
|
}
|
|
|
|
/**
|
|
* If the user clicks to fast on adding item,
|
|
* we need to re-fetch the rating from IMDb.
|
|
*
|
|
* @param $data
|
|
*
|
|
* @return float|null
|
|
*/
|
|
private function parseImdbRating($data)
|
|
{
|
|
if( ! isset($data['imdb_rating'])) {
|
|
$imdbId = $data['imdb_id'];
|
|
|
|
if($imdbId) {
|
|
return $this->imdb->parseRating($imdbId);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Otherwise we already have the rating saved.
|
|
return $data['imdb_rating'];
|
|
}
|
|
|
|
/**
|
|
* TV shows needs an extra append for external ids.
|
|
*
|
|
* @param $details
|
|
* @return mixed
|
|
*/
|
|
public function parseImdbId($details)
|
|
{
|
|
return $details->external_ids->imdb_id ?? ($details->imdb_id ?? null);
|
|
}
|
|
|
|
/**
|
|
* Get the key for the youtube trailer video. Fallback with english trailer.
|
|
*
|
|
* @param $details
|
|
* @param $mediaType
|
|
* @return string|null
|
|
*/
|
|
public function parseYoutubeKey($details, $mediaType)
|
|
{
|
|
if(isset($details->videos->results[0])) {
|
|
return $details->videos->results[0]->key;
|
|
}
|
|
|
|
// Try to fetch details again with english language as fallback.
|
|
$videos = $this->tmdb->videos($details->id, $mediaType, 'en');
|
|
|
|
return $videos->results[0]->key ?? null;
|
|
}
|
|
|
|
/**
|
|
* @param $data
|
|
* @param $mediaType
|
|
* @return Item
|
|
*/
|
|
public function createEmpty($data, $mediaType)
|
|
{
|
|
$mediaType = mediaType($mediaType);
|
|
|
|
$data = [
|
|
'name' => getFileName($data),
|
|
'src' => $data->changed->src ?? $data->src,
|
|
'subtitles' => $data->changed->subtitles ?? $data->subtitles,
|
|
];
|
|
|
|
return $this->model->storeEmpty($data, $mediaType);
|
|
}
|
|
|
|
/**
|
|
* Delete movie or tv show (with episodes and alternative titles).
|
|
* Also remove the poster image file.
|
|
*
|
|
* @param $itemId
|
|
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
|
*/
|
|
public function remove($itemId)
|
|
{
|
|
$item = $this->model->find($itemId);
|
|
|
|
if( ! $item) {
|
|
return response('Not Found', Response::HTTP_NOT_FOUND);
|
|
}
|
|
|
|
$tmdbId = $item->tmdb_id;
|
|
|
|
$item->delete();
|
|
|
|
// Delete all related episodes, alternative titles and images.
|
|
$this->episodeService->remove($tmdbId);
|
|
$this->alternativeTitleService->remove($tmdbId);
|
|
$this->storage->removeImages($item->poster, $item->backdrop);
|
|
}
|
|
|
|
/**
|
|
* Return all items with pagination.
|
|
*
|
|
* @param $type
|
|
* @param $orderBy
|
|
* @return mixed
|
|
*/
|
|
public function getWithPagination($type, $orderBy)
|
|
{
|
|
$orderType = $orderBy == 'rating' ? 'asc' : 'desc';
|
|
|
|
$items = $this->model->orderBy($orderBy, $orderType)->with('latestEpisode')->withCount('episodesWithSrc');
|
|
|
|
if($type == 'tv' || $type == 'movie') {
|
|
$items = $items->where('media_type', $type);
|
|
}
|
|
|
|
return $items->simplePaginate(config('app.LOADING_ITEMS'));
|
|
}
|
|
|
|
/**
|
|
* Update rating for a movie.
|
|
*
|
|
* @param $itemId
|
|
* @param $rating
|
|
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
|
*/
|
|
public function changeRating($itemId, $rating)
|
|
{
|
|
$item = $this->model->find($itemId);
|
|
|
|
if( ! $item) {
|
|
return response('Not Found', Response::HTTP_NOT_FOUND);
|
|
}
|
|
|
|
// Update the parent relation only if we change rating from neutral.
|
|
if($item->rating == 0) {
|
|
$this->model->updateLastSeenAt($item->tmdb_id);
|
|
}
|
|
|
|
$item->update([
|
|
'rating' => $rating,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Search for all items by title in our database.
|
|
*
|
|
* @param $title
|
|
* @return mixed
|
|
*/
|
|
public function search($title)
|
|
{
|
|
return $this->model->findByTitle($title)->with('latestEpisode')->withCount('episodesWithSrc')->get();
|
|
}
|
|
|
|
/**
|
|
* Parse full genre list of all movies and tv shows in our database and save them.
|
|
*/
|
|
public function updateGenre()
|
|
{
|
|
increaseTimeLimit();
|
|
|
|
$items = $this->model->all();
|
|
|
|
$items->each(function($item) {
|
|
$genres = $this->tmdb->details($item->tmdb_id, $item->media_type)->genres;
|
|
$data = collect($genres)->pluck('name')->all();
|
|
|
|
$item->update([
|
|
'genre' => implode($data, ', '),
|
|
]);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* See if we can find a item by title, fp_name, tmdb_id or src in our database.
|
|
*
|
|
* If we search from file-parser, we also need to filter the results by media_type.
|
|
* If we have e.g. 'Avatar' as tv show, we don't want results like the 'Avatar' movie.
|
|
*
|
|
* @param $type
|
|
* @param $value
|
|
* @param $mediaType
|
|
* @return mixed
|
|
*/
|
|
public function findBy($type, $value, $mediaType = null)
|
|
{
|
|
if($mediaType) {
|
|
$mediaType = mediaType($mediaType);
|
|
}
|
|
|
|
switch($type) {
|
|
case 'title':
|
|
return $this->model->findByTitle($value, $mediaType)->first();
|
|
case 'title_strict':
|
|
return $this->model->findByTitleStrict($value, $mediaType)->first();
|
|
case 'fp_name':
|
|
return $this->model->findByFPName($value, $mediaType)->first();
|
|
case 'tmdb_id':
|
|
return $this->model->findByTmdbId($value)->with('latestEpisode')->first();
|
|
case 'src':
|
|
return $this->model->findBySrc($value)->first();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|