mirror of
https://github.com/devfake/flox.git
synced 2024-11-15 06:32:34 +01:00
search and create episodes
This commit is contained in:
parent
0bf58b12f6
commit
5cc5802231
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Episode;
|
||||||
use App\Item;
|
use App\Item;
|
||||||
use App\Services\Storage;
|
use App\Services\Storage;
|
||||||
use App\Services\TMDB;
|
use App\Services\TMDB;
|
||||||
@ -27,16 +28,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all items for home with pagination.
|
* Return all items with pagination.
|
||||||
*
|
*
|
||||||
* @param $orderBy
|
* @param $orderBy
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function items($orderBy)
|
public function items($type, $orderBy)
|
||||||
{
|
{
|
||||||
$orderType = $orderBy == 'rating' ? 'asc' : 'desc';
|
$orderType = $orderBy == 'rating' ? 'asc' : 'desc';
|
||||||
|
|
||||||
return $this->item->orderBy($orderBy, $orderType)->simplePaginate($this->loadingItems);
|
$item = $this->item->orderBy($orderBy, $orderType)->with('latestEpisode');
|
||||||
|
|
||||||
|
if($type != 'home') {
|
||||||
|
$item = $item->where('media_type', $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item->simplePaginate($this->loadingItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all Episodes of an tv show.
|
||||||
|
*
|
||||||
|
* @param $tmdb_id
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function episodes($tmdb_id)
|
||||||
|
{
|
||||||
|
return Episode::where('tmdb_id', $tmdb_id)->get()->groupBy('season_number');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +111,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete movie in database and delete the poster image file.
|
* Delete movie or tv show (with episodes) and remove the poster image file.
|
||||||
*
|
*
|
||||||
* @param $itemID
|
* @param $itemID
|
||||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
||||||
@ -107,8 +125,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->storage->removePosterFile($item->poster);
|
$this->storage->removePosterFile($item->poster);
|
||||||
|
$tmdb_id = $item->tmdb_id;
|
||||||
|
|
||||||
$item->delete();
|
$item->delete();
|
||||||
|
|
||||||
|
// Delete all related episodes
|
||||||
|
// todo: Make this possible in migrations
|
||||||
|
Episode::where('tmdb_id', $tmdb_id)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,11 +140,15 @@
|
|||||||
* @param $data
|
* @param $data
|
||||||
* @return Item
|
* @return Item
|
||||||
*/
|
*/
|
||||||
private function createItem($data)
|
private function createItem($data, TMDB $tmdb)
|
||||||
{
|
{
|
||||||
return $this->item->create([
|
$tmdbId = $data['tmdb_id'];
|
||||||
'tmdb_id' => $data['tmdb_id'],
|
$mediaType = $data['media_type'];
|
||||||
|
|
||||||
|
$item = $this->item->create([
|
||||||
|
'tmdb_id' => $tmdbId,
|
||||||
'title' => $data['title'],
|
'title' => $data['title'],
|
||||||
|
'media_type' => $mediaType,
|
||||||
'original_title' => $data['original_title'],
|
'original_title' => $data['original_title'],
|
||||||
'poster' => $data['poster'],
|
'poster' => $data['poster'],
|
||||||
'rating' => 1,
|
'rating' => 1,
|
||||||
@ -129,5 +156,45 @@
|
|||||||
'genre' => $data['genre'],
|
'genre' => $data['genre'],
|
||||||
'created_at' => time(),
|
'created_at' => time(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if($mediaType == 'tv') {
|
||||||
|
$this->createEpisodes($tmdbId, $tmdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSeen($id)
|
||||||
|
{
|
||||||
|
$episode = Episode::find($id);
|
||||||
|
$episode->seen = ! $episode->seen;
|
||||||
|
|
||||||
|
if( ! $episode->save()) {
|
||||||
|
return response('Server Error', 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save all episodes of each season.
|
||||||
|
*
|
||||||
|
* @param $seasons
|
||||||
|
* @param $tmdbId
|
||||||
|
*/
|
||||||
|
private function createEpisodes($tmdbId, TMDB $tmdb)
|
||||||
|
{
|
||||||
|
$seasons = $tmdb->tvEpisodes($tmdbId);
|
||||||
|
|
||||||
|
foreach($seasons as $season) {
|
||||||
|
foreach($season->episodes as $episode) {
|
||||||
|
$new = new Episode();
|
||||||
|
$new->season_tmdb_id = $season->id;
|
||||||
|
$new->episode_tmdb_id = $episode->id;
|
||||||
|
$new->season_number = $episode->season_number;
|
||||||
|
$new->episode_number = $episode->episode_number;
|
||||||
|
$new->name = $episode->name;
|
||||||
|
$new->tmdb_id = $tmdbId;
|
||||||
|
$new->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,10 +18,22 @@
|
|||||||
'original_title',
|
'original_title',
|
||||||
'poster',
|
'poster',
|
||||||
'media_type',
|
'media_type',
|
||||||
'tv_data',
|
|
||||||
'rating',
|
'rating',
|
||||||
'released',
|
'released',
|
||||||
'created_at',
|
'created_at',
|
||||||
'genre',
|
'genre',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function episodes()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Episode::class, 'tmdb_id', 'tmdb_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function latestEpisode()
|
||||||
|
{
|
||||||
|
return $this->hasOne(Episode::class, 'tmdb_id', 'tmdb_id')
|
||||||
|
->orderBy('id', 'desc')
|
||||||
|
->where('seen', true)
|
||||||
|
->latest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
private $client;
|
private $client;
|
||||||
private $apiKey;
|
private $apiKey;
|
||||||
|
private $translation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the API Key for TMDB and create an instance of Guzzle.
|
* Get the API Key for TMDB and create an instance of Guzzle.
|
||||||
@ -17,24 +18,23 @@
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->apiKey = config('app.TMDB_API_KEY');
|
$this->apiKey = config('app.TMDB_API_KEY');
|
||||||
|
$this->translation = config('app.TRANSLATION');
|
||||||
$this->client = new Client(['base_uri' => 'http://api.themoviedb.org/']);
|
$this->client = new Client(['base_uri' => 'http://api.themoviedb.org/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search TMDB for an movie by 'title'.
|
* Search TMDB by 'title'.
|
||||||
*
|
*
|
||||||
* @param $title
|
* @param $title
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function search($title)
|
public function search($title)
|
||||||
{
|
{
|
||||||
$translation = config('app.TRANSLATION');
|
$response = $this->client->get('/3/search/multi', [
|
||||||
|
|
||||||
$response = $this->client->get('/3/search/movie', [
|
|
||||||
'query' => [
|
'query' => [
|
||||||
'api_key' => $this->apiKey,
|
'api_key' => $this->apiKey,
|
||||||
'query' => $title,
|
'query' => $title,
|
||||||
'language' => strtolower($translation)
|
'language' => strtolower($this->translation)
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -54,13 +54,14 @@
|
|||||||
/**
|
/**
|
||||||
* Search TMDB for recommendations and similar movies.
|
* Search TMDB for recommendations and similar movies.
|
||||||
*
|
*
|
||||||
|
* @param $mediaType
|
||||||
* @param $tmdbID
|
* @param $tmdbID
|
||||||
* @return \Illuminate\Support\Collection
|
* @return \Illuminate\Support\Collection
|
||||||
*/
|
*/
|
||||||
public function suggestions($tmdbID)
|
public function suggestions($mediaType, $tmdbID)
|
||||||
{
|
{
|
||||||
$recommendations = $this->searchSuggestions($tmdbID, 'recommendations');
|
$recommendations = $this->searchSuggestions($mediaType, $tmdbID, 'recommendations');
|
||||||
$similar = $this->searchSuggestions($tmdbID, 'similar');
|
$similar = $this->searchSuggestions($mediaType, $tmdbID, 'similar');
|
||||||
|
|
||||||
$items = $recommendations->merge($similar);
|
$items = $recommendations->merge($similar);
|
||||||
|
|
||||||
@ -73,16 +74,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param $mediaType
|
||||||
* @param $tmdbID
|
* @param $tmdbID
|
||||||
* @param $type
|
* @param $type
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Support\Collection
|
* @return \Illuminate\Support\Collection
|
||||||
*/
|
*/
|
||||||
private function searchSuggestions($tmdbID, $type)
|
private function searchSuggestions($mediaType, $tmdbID, $type)
|
||||||
{
|
{
|
||||||
$response = $this->client->get('/3/movie/' . $tmdbID . '/' . $type, ['query' => ['api_key' => $this->apiKey]]);
|
$response = $this->client->get('/3/' . $mediaType . '/' . $tmdbID . '/' . $type, [
|
||||||
|
'query' => [
|
||||||
|
'api_key' => $this->apiKey,
|
||||||
|
'language' => strtolower($this->translation)
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
return collect($this->createItems($response));
|
return collect($this->createItems($response, $mediaType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +100,12 @@
|
|||||||
*/
|
*/
|
||||||
private function searchTrendingOrUpcoming($type)
|
private function searchTrendingOrUpcoming($type)
|
||||||
{
|
{
|
||||||
$response = $this->client->get('/3/movie/' . $type, ['query' => ['api_key' => $this->apiKey]]);
|
$response = $this->client->get('/3/movie/' . $type, [
|
||||||
|
'query' => [
|
||||||
|
'api_key' => $this->apiKey,
|
||||||
|
'language' => strtolower($this->translation)
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
$items = collect($this->createItems($response));
|
$items = collect($this->createItems($response));
|
||||||
$allID = $items->pluck('tmdb_id');
|
$allID = $items->pluck('tmdb_id');
|
||||||
@ -116,20 +128,32 @@
|
|||||||
* @param $response
|
* @param $response
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function createItems($response)
|
private function createItems($response, $type = null)
|
||||||
{
|
{
|
||||||
$items = [];
|
$items = [];
|
||||||
$response = json_decode($response->getBody());
|
$response = json_decode($response->getBody());
|
||||||
|
|
||||||
foreach($response->results as $result) {
|
foreach($response->results as $result) {
|
||||||
$dtime = DateTime::createFromFormat('Y-m-d', ($result->release_date ?: '1970-12-1'));
|
// Suggestions doesn't deliver 'media type' by default
|
||||||
|
$mediaType = $type ?: $result->media_type;
|
||||||
|
|
||||||
|
if($mediaType == 'person') continue;
|
||||||
|
|
||||||
|
$dtime = DateTime::createFromFormat('Y-m-d', (array_key_exists('release_date', $result)
|
||||||
|
? ($result->release_date ?: '1970-12-1')
|
||||||
|
: ($result->first_air_date ?: '1970-12-1')
|
||||||
|
));
|
||||||
|
|
||||||
|
// 'name' is from tv, 'title' from movies
|
||||||
$items[] = [
|
$items[] = [
|
||||||
'tmdb_id' => $result->id,
|
'tmdb_id' => $result->id,
|
||||||
'title' => $result->title,
|
'title' => array_key_exists('name', $result) ? $result->name : $result->title,
|
||||||
'original_title' => $result->original_title,
|
'original_title' => array_key_exists('name', $result) ? $result->original_name : $result->original_title,
|
||||||
'poster' => $result->poster_path,
|
'poster' => $result->poster_path,
|
||||||
|
'media_type' => $mediaType,
|
||||||
'released' => $dtime->getTimestamp(),
|
'released' => $dtime->getTimestamp(),
|
||||||
'genre' => $this->parseGenre($result->genre_ids),
|
'genre' => $this->parseGenre($result->genre_ids),
|
||||||
|
'episodes' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,11 +168,69 @@
|
|||||||
*/
|
*/
|
||||||
public function movie($tmdb_id)
|
public function movie($tmdb_id)
|
||||||
{
|
{
|
||||||
$response = $this->client->get('/3/movie/' . $tmdb_id, ['query' => ['api_key' => $this->apiKey]]);
|
$response = $this->client->get('/3/movie/' . $tmdb_id, [
|
||||||
|
'query' => [
|
||||||
|
'api_key' => $this->apiKey,
|
||||||
|
'language' => strtolower($this->translation)
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
return json_decode($response->getBody());
|
return json_decode($response->getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current count of seasons.
|
||||||
|
*
|
||||||
|
* @param $result
|
||||||
|
* @return integer | null
|
||||||
|
*/
|
||||||
|
private function tvSeasonsCount($id, $mediaType)
|
||||||
|
{
|
||||||
|
if($mediaType == 'tv') {
|
||||||
|
$response = $this->client->get('/3/tv/' . $id, [
|
||||||
|
'query' => [
|
||||||
|
'api_key' => $this->apiKey,
|
||||||
|
'language' => strtolower($this->translation)
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$seasons = collect(json_decode($response->getBody())->seasons);
|
||||||
|
|
||||||
|
return $seasons->filter(function ($season) {
|
||||||
|
// We don't need pilots
|
||||||
|
return $season->season_number > 0;
|
||||||
|
})->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all episodes of each season.
|
||||||
|
*
|
||||||
|
* @param $id
|
||||||
|
* @param $seasons
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function tvEpisodes($id)
|
||||||
|
{
|
||||||
|
$seasons = $this->tvSeasonsCount($id, 'tv');
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
for($i = 1; $i <= $seasons; $i++) {
|
||||||
|
$response = $this->client->get('/3/tv/' . $id . '/season/' . $i, [
|
||||||
|
'query' => [
|
||||||
|
'api_key' => $this->apiKey,
|
||||||
|
'language' => strtolower($this->translation)
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$data[$i] = json_decode($response->getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create genre string from genre_ids.
|
* Create genre string from genre_ids.
|
||||||
*
|
*
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
Route::post('/login', 'UserController@login');
|
Route::post('/login', 'UserController@login');
|
||||||
Route::get('/logout', 'UserController@logout');
|
Route::get('/logout', 'UserController@logout');
|
||||||
|
|
||||||
Route::get('/items/{orderBy}', 'ItemController@items');
|
Route::get('/items/{type}/{orderBy}', 'ItemController@items');
|
||||||
Route::get('/search-items', 'ItemController@search');
|
Route::get('/search-items', 'ItemController@search');
|
||||||
|
|
||||||
Route::get('/suggestions/{tmdbID}', 'TMDBController@suggestions');
|
Route::get('/suggestions/{tmdbID}/{mediaType}', 'TMDBController@suggestions');
|
||||||
Route::get('/trending', 'TMDBController@trending');
|
Route::get('/trending', 'TMDBController@trending');
|
||||||
Route::get('/upcoming', 'TMDBController@upcoming');
|
Route::get('/upcoming', 'TMDBController@upcoming');
|
||||||
|
|
||||||
@ -22,6 +22,8 @@
|
|||||||
Route::patch('/settings', 'SettingController@changeSettings');
|
Route::patch('/settings', 'SettingController@changeSettings');
|
||||||
|
|
||||||
Route::patch('/userdata', 'UserController@changeUserData');
|
Route::patch('/userdata', 'UserController@changeUserData');
|
||||||
|
Route::patch('/set-seen/{id}', 'ItemController@setSeen');
|
||||||
|
Route::get('/episodes/{tmdb_id}', 'ItemController@episodes');
|
||||||
|
|
||||||
Route::get('/search-tmdb', 'TMDBController@search');
|
Route::get('/search-tmdb', 'TMDBController@search');
|
||||||
Route::post('/add', 'ItemController@add');
|
Route::post('/add', 'ItemController@add');
|
||||||
|
@ -10,15 +10,15 @@
|
|||||||
<i class="icon-add" v-if=" ! rated"></i>
|
<i class="icon-add" v-if=" ! rated"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<router-link :to="'/suggestions?for=' + localItem.tmdb_id" class="recommend-item">{{ lang('suggestions') }}</router-link>
|
<router-link :to="'/suggestions?for=' + localItem.tmdb_id + '&type=' + localItem.media_type" class="recommend-item">{{ lang('suggestions') }}</router-link>
|
||||||
<span class="remove-item" v-if="localItem.rating && auth" @click="removeItem()">{{ lang('delete movie') }}</span>
|
<span class="remove-item" v-if="localItem.rating && auth" @click="removeItem()">{{ lang('delete movie') }}</span>
|
||||||
|
|
||||||
<img v-if="localItem.poster" :src="poster" class="item-image" width="185" height="278">
|
<img v-if="localItem.poster" :src="poster" class="item-image" width="185" height="278">
|
||||||
<img v-if=" ! localItem.poster" :src="noImage" class="item-image" width="185" height="278">
|
<img v-if=" ! localItem.poster" :src="noImage" class="item-image" width="185" height="278">
|
||||||
|
|
||||||
<span class="show-episode" @click="changeEpisode()" v-if="localItem.type == 'tv'">
|
<span class="show-episode" @click="editEpisodes()" v-if="localItem.media_type == 'tv' && localItem.rating">
|
||||||
<span class="season-item"><i>S</i>1/2</span>
|
<span class="season-item"><i>S</i>{{ season }}</span>
|
||||||
<span class="episode-item"><i>E</i>03/10</span>
|
<span class="episode-item"><i>E</i>{{ episode }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -35,6 +35,8 @@
|
|||||||
import http from 'axios';
|
import http from 'axios';
|
||||||
import Helper from '../../helper';
|
import Helper from '../../helper';
|
||||||
|
|
||||||
|
import { mapMutations, mapActions } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [Helper],
|
mixins: [Helper],
|
||||||
|
|
||||||
@ -43,6 +45,7 @@
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
localItem: this.item,
|
localItem: this.item,
|
||||||
|
latestEpisode: this.item.latest_episode,
|
||||||
saveTimeout: null,
|
saveTimeout: null,
|
||||||
auth: config.auth,
|
auth: config.auth,
|
||||||
prevRating: null,
|
prevRating: null,
|
||||||
@ -83,14 +86,46 @@
|
|||||||
|
|
||||||
youtube() {
|
youtube() {
|
||||||
return `https://www.youtube.com/results?search_query=${this.localItem.title} ${this.released} Trailer`;
|
return `https://www.youtube.com/results?search_query=${this.localItem.title} ${this.released} Trailer`;
|
||||||
|
},
|
||||||
|
|
||||||
|
season() {
|
||||||
|
if(this.latestEpisode) {
|
||||||
|
return this.addZero(this.latestEpisode.season_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '01';
|
||||||
|
},
|
||||||
|
|
||||||
|
episode() {
|
||||||
|
if(this.latestEpisode) {
|
||||||
|
return this.addZero(this.latestEpisode.episode_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '0';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
changeEpisode()
|
...mapMutations([ 'OPEN_MODAL' ]),
|
||||||
{
|
...mapActions([ 'fetchEpisodes' ]),
|
||||||
|
|
||||||
|
editEpisodes() {
|
||||||
|
this.fetchEpisodes({
|
||||||
|
tmdb_id: this.localItem.tmdb_id,
|
||||||
|
title: this.localItem.title
|
||||||
|
});
|
||||||
|
this.openModal();
|
||||||
|
},
|
||||||
|
|
||||||
|
openModal() {
|
||||||
if(this.auth) {
|
if(this.auth) {
|
||||||
console.log("changed");
|
this.OPEN_MODAL({
|
||||||
|
type: 'season',
|
||||||
|
data: {
|
||||||
|
tmdb_id: this.localItem.tmdb_id,
|
||||||
|
title: this.localItem.title
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -122,6 +157,7 @@
|
|||||||
http.post(`${config.api}/add`, {item: this.localItem}).then(value => {
|
http.post(`${config.api}/add`, {item: this.localItem}).then(value => {
|
||||||
this.localItem = value.data;
|
this.localItem = value.data;
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
|
this.rated = false;
|
||||||
}, error => {
|
}, error => {
|
||||||
if(error.status == 409) {
|
if(error.status == 409) {
|
||||||
alert(this.localItem.title + ' already exists!');
|
alert(this.localItem.title + ' already exists!');
|
||||||
|
Loading…
Reference in New Issue
Block a user