From 3279df93ea2cd24cc73702ebfb3eded45589957f Mon Sep 17 00:00:00 2001 From: HDVinnie Date: Mon, 6 Dec 2021 08:40:40 -0500 Subject: [PATCH] (Add) Torrent Cards Search View --- .../Controllers/TorrentCardController.php | 25 + app/Http/Livewire/TorrentCardSearch.php | 374 +++++++++++ .../livewire/torrent-card-search.blade.php | 629 ++++++++++++++++++ resources/views/torrent/cards.blade.php | 23 + routes/web.php | 1 + 5 files changed, 1052 insertions(+) create mode 100644 app/Http/Controllers/TorrentCardController.php create mode 100644 app/Http/Livewire/TorrentCardSearch.php create mode 100644 resources/views/livewire/torrent-card-search.blade.php create mode 100644 resources/views/torrent/cards.blade.php diff --git a/app/Http/Controllers/TorrentCardController.php b/app/Http/Controllers/TorrentCardController.php new file mode 100644 index 000000000..50ca791c9 --- /dev/null +++ b/app/Http/Controllers/TorrentCardController.php @@ -0,0 +1,25 @@ + + * @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0 + */ + +namespace App\Http\Controllers; + +class TorrentCardController extends Controller +{ + /** + * Display a listing of the resource. + */ + public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application + { + return \view('torrent.cards'); + } +} diff --git a/app/Http/Livewire/TorrentCardSearch.php b/app/Http/Livewire/TorrentCardSearch.php new file mode 100644 index 000000000..8f6fbcd88 --- /dev/null +++ b/app/Http/Livewire/TorrentCardSearch.php @@ -0,0 +1,374 @@ + + * @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0 + */ + +namespace App\Http\Livewire; + +use App\Models\User; +use App\Models\Wish; +use Livewire\Component; +use App\Models\Torrent; +use App\Models\Keyword; +use App\Models\History; +use App\Models\Category; +use App\Models\Bookmark; +use Livewire\WithPagination; +use App\Models\PlaylistTorrent; +use App\Models\PersonalFreeleech; +use Illuminate\Support\Facades\DB; + +class TorrentCardSearch extends Component +{ + use WithPagination; + + public $name = ''; + + public $description = ''; + + public $mediainfo = ''; + + public $uploader = ''; + + public $keywords = []; + + public $startYear = ''; + + public $endYear = ''; + + public $categories = []; + + public $types = []; + + public $resolutions = []; + + public $genres = []; + + public $regions = []; + + public $distributors = []; + + public $tmdbId = ''; + + public $imdbId = ''; + + public $tvdbId = ''; + + public $malId = ''; + + public $playlistId = ''; + + public $collectionId = ''; + + public $free; + + public $doubleup; + + public $featured; + + public $stream; + + public $sd; + + public $highspeed; + + public $bookmarked; + + public $wished; + + public $internal; + + public $personalRelease; + + public $alive; + + public $dying; + + public $dead; + + public $notDownloaded; + + public $downloaded; + + public $seeding; + + public $leeching; + + public $incomplete; + + public $perPage = 24; + + public $sortField = 'bumped_at'; + + public $sortDirection = 'desc'; + + protected $queryString = [ + 'name' => ['except' => ''], + 'description' => ['except' => ''], + 'mediainfo' => ['except' => ''], + 'uploader' => ['except' => ''], + 'keywords' => ['except' => []], + 'startYear' => ['except' => ''], + 'endYear' => ['except' => ''], + 'categories' => ['except' => []], + 'types' => ['except' => []], + 'resolutions' => ['except' => []], + 'genres' => ['except' => []], + 'regions' => ['except' => []], + 'distributors' => ['except' => []], + 'tmdbId' => ['except' => ''], + 'imdbId' => ['except' => ''], + 'tvdbId' => ['except' => ''], + 'malId' => ['except' => ''], + 'playlistId' => ['except' => ''], + 'collectionId' => ['except' => ''], + 'free' => ['except' => false], + 'doubleup' => ['except' => false], + 'featured' => ['except' => false], + 'stream' => ['except' => false], + 'sd' => ['except' => false], + 'highspeed' => ['except' => false], + 'bookmarked' => ['except' => false], + 'wished' => ['except' => false], + 'internal' => ['except' => false], + 'personalRelease' => ['except' => false], + 'alive' => ['except' => false], + 'dying' => ['except' => false], + 'dead' => ['except' => false], + 'downloaded' => ['except' => false], + 'seeding' => ['except' => false], + 'leeching' => ['except' => false], + 'incomplete' => ['except' => false], + 'sortField' => ['except' => 'bumped_at'], + 'sortDirection' => ['except' => 'desc'], + 'page' => ['except' => 1], + 'perPage' => ['except' => ''], + ]; + + protected $rules = [ + 'genres.*' => 'exists:genres,id', + ]; + + final public function paginationView(): string + { + return 'vendor.pagination.livewire-pagination'; + } + + final public function updatedPage(): void + { + $this->emit('paginationChanged'); + } + + final public function updatingName(): void + { + $this->resetPage(); + } + + final public function getTorrentsStatProperty() + { + return DB::table('torrents') + ->selectRaw('count(*) as total') + ->selectRaw('count(case when seeders > 0 then 1 end) as alive') + ->selectRaw('count(case when seeders = 0 then 1 end) as dead') + ->first(); + } + + final public function getPersonalFreeleechProperty() + { + return PersonalFreeleech::where('user_id', '=', \auth()->user()->id)->first(); + } + + final public function getTorrentsProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator + { + return Torrent::with(['user:id,username,group_id', 'category', 'type', 'resolution']) + ->when($this->name, function ($query) { + $terms = \explode(' ', $this->name); + $search = ''; + foreach ($terms as $term) { + $search .= '%'.$term.'%'; + } + + $query->where('name', 'LIKE', $search); + }) + ->when($this->description, function ($query) { + $query->where('description', 'LIKE', '%'.$this->description.'%'); + }) + ->when($this->mediainfo, function ($query) { + $query->where('mediainfo', 'LIKE', '%'.$this->mediainfo.'%'); + }) + ->when($this->uploader, function ($query) { + $match = User::where('username', 'LIKE', '%'.$this->uploader.'%')->orderBy('username', 'ASC')->first(); + if ($match) { + $query->where('user_id', '=', $match->id)->where('anon', '=', 0); + } + }) + ->when($this->keywords, function ($query) { + $keywords = self::parseKeywords($this->keywords); + $keyword = Keyword::whereIn('name', $keywords)->pluck('torrent_id'); + $query->whereIn('id', $keyword); + }) + ->when($this->startYear && $this->endYear, function ($query) { + $query->whereBetween('release_year', [$this->startYear, $this->endYear]); + }) + ->when($this->categories, function ($query) { + $query->whereIn('category_id', $this->categories); + }) + ->when($this->types, function ($query) { + $query->whereIn('type_id', $this->types); + }) + ->when($this->resolutions, function ($query) { + $query->whereIn('resolution_id', $this->resolutions); + }) + ->when($this->genres, function ($query) { + $this->validate(); + + $tvCollection = DB::table('genre_tv')->whereIn('genre_id', $this->genres)->pluck('tv_id'); + $movieCollection = DB::table('genre_movie')->whereIn('genre_id', $this->genres)->pluck('movie_id'); + $mergedCollection = $tvCollection->merge($movieCollection); + + $query->whereRaw("tmdb in ('".\implode("','", $mergedCollection->toArray())."')"); // Protected with Validation that IDs passed are not malicious + }) + ->when($this->regions, function ($query) { + $query->whereIn('region_id', $this->regions); + }) + ->when($this->distributors, function ($query) { + $query->whereIn('distributor_id', $this->distributors); + }) + ->when($this->tmdbId === '0' || $this->tmdbId, function ($query) { + $query->where('tmdb', '=', $this->tmdbId); + }) + ->when($this->imdbId === '0' || $this->imdbId, function ($query) { + $query->where('imdb', '=', $this->imdbId); + }) + ->when($this->tvdbId === '0' || $this->tvdbId, function ($query) { + $query->where('tvdb', '=', $this->tvdbId); + }) + ->when($this->malId === '0' || $this->malId, function ($query) { + $query->where('mal', '=', $this->malId); + }) + ->when($this->playlistId, function ($query) { + $playlist = PlaylistTorrent::where('playlist_id', '=', $this->playlistId)->pluck('torrent_id'); + $query->whereIn('id', $playlist); + }) + ->when($this->collectionId, function ($query) { + $categories = Category::where('movie_meta', '=', 1)->pluck('id'); + $collection = DB::table('collection_movie')->where('collection_id', '=', $this->collectionId)->pluck('movie_id'); + $query->whereIn('category_id', $categories)->whereIn('tmdb', $collection); + }) + ->when($this->free, function ($query) { + $query->where('free', '=', 1); + }) + ->when($this->doubleup, function ($query) { + $query->where('doubleup', '=', 1); + }) + ->when($this->featured, function ($query) { + $query->where('featured', '=', 1); + }) + ->when($this->stream, function ($query) { + $query->where('stream', '=', 1); + }) + ->when($this->sd, function ($query) { + $query->where('sd', '=', 1); + }) + ->when($this->highspeed, function ($query) { + $query->where('highspeed', '=', 1); + }) + ->when($this->bookmarked, function ($query) { + $bookmarks = Bookmark::where('user_id', '=', \auth()->user()->id)->pluck('torrent_id'); + $query->whereIn('id', $bookmarks); + }) + ->when($this->wished, function ($query) { + $wishes = Wish::where('user_id', '=', \auth()->user()->id)->pluck('tmdb'); + $query->whereIn('tmdb', $wishes); + }) + ->when($this->internal, function ($query) { + $query->where('internal', '=', 1); + }) + ->when($this->personalRelease, function ($query) { + $query->where('personal_release', '=', 1); + }) + ->when($this->alive, function ($query) { + $query->where('seeders', '>=', 1); + }) + ->when($this->dying, function ($query) { + $query->where('seeders', '=', 1)->where('times_completed', '>=', 3); + }) + ->when($this->dead, function ($query) { + $query->where('seeders', '=', 0); + }) + ->when($this->notDownloaded, function ($query) { + $history = History::where('user_id', '=', \auth()->user()->id)->pluck('info_hash')->toArray(); + if (! $history || ! \is_array($history)) { + $history = []; + } + + $query->whereNotIn('info_hash', $history); + }) + ->when($this->downloaded, function ($query) { + $query->whereHas('history', function ($query) { + $query->where('user_id', '=', \auth()->user()->id); + }); + }) + ->when($this->seeding, function ($query) { + $query->whereHas('history', function ($q) { + $q->where('user_id', '=', \auth()->user()->id)->where('active', '=', true)->where('seeder', '=', true); + }); + }) + ->when($this->leeching, function ($query) { + $query->whereHas('history', function ($q) { + $q->where('user_id', '=', \auth()->user()->id)->where('active', '=', true)->where('seedtime', '=', '0'); + }); + }) + ->when($this->incomplete, function ($query) { + $query->whereHas('history', function ($q) { + $q->where('user_id', '=', \auth()->user()->id)->where('active', '=', false)->where('seeder', '=', false)->where('seedtime', '=', '0'); + }); + }) + ->orderBy('sticky', 'desc') + ->orderBy($this->sortField, $this->sortDirection) + ->paginate($this->perPage); + } + + private static function parseKeywords($text): array + { + $parts = \explode(', ', $text); + $result = []; + foreach ($parts as $part) { + $part = \trim($part); + if ($part !== '') { + $result[] = $part; + } + } + + return $result; + } + + final public function sortBy($field): void + { + if ($this->sortField === $field) { + $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'; + } else { + $this->sortDirection = 'asc'; + } + + $this->sortField = $field; + } + + final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application + { + return \view('livewire.torrent-card-search', [ + 'user' => User::with('history')->findOrFail(\auth()->user()->id), + 'torrents' => $this->torrents, + 'torrentsStat' => $this->torrentsStat, + 'personalFreeleech' => $this->personalFreeleech, + ]); + } +} diff --git a/resources/views/livewire/torrent-card-search.blade.php b/resources/views/livewire/torrent-card-search.blade.php new file mode 100644 index 000000000..711e972c7 --- /dev/null +++ b/resources/views/livewire/torrent-card-search.blade.php @@ -0,0 +1,629 @@ +
+
+ + +
+
+
+ {{ $torrents->links() }} +
+
+
+ + Total: {{ \number_format($torrentsStat->total) }} | + Alive: {{ \number_format($torrentsStat->alive) }} | + Dead: {{ \number_format($torrentsStat->dead) }} | + + +
+
+
+ @lang('torrent.torrents') +
+
+
+ + + + + + + + + + + + + + + +
+
+ @lang('common.name') + @include('livewire.includes._sort-icon', ['field' => 'name']) +
+
+
+ +
+
+
+ +
+
+
+ + @include('livewire.includes._sort-icon', ['field' => 'size']) +
+
+
+ + @include('livewire.includes._sort-icon', ['field' => 'seeders']) +
+
+
+ + @include('livewire.includes._sort-icon', ['field' => 'leechers']) +
+
+
+ + @include('livewire.includes._sort-icon', ['field' => 'times_completed']) +
+
+
+ @lang('common.created_at') + @include('livewire.includes._sort-icon', ['field' => 'created_at']) +
+
+ @foreach($torrents as $torrent) + @php $meta = null; @endphp + @if ($torrent->category->tv_meta) + @if ($torrent->tmdb || $torrent->tmdb != 0) + @php + $meta = cache()->remember('tv.'.$torrent->tmdb, 3_600, function() use ($torrent) { + return App\Models\Tv::where('id', '=', $torrent->tmdb)->first(); + }); + @endphp + @endif + @endif + @if ($torrent->category->movie_meta) + @if ($torrent->tmdb || $torrent->tmdb != 0) + @php + $meta = cache()->remember('movie.'.$torrent->tmdb, 3_600, function() use ($torrent) { + return App\Models\Movie::where('id', '=', $torrent->tmdb)->first(); + }); + @endphp + @endif + @endif + @if ($torrent->category->game_meta) + @if ($torrent->igdb || $torrent->igdb != 0) + @php $meta = MarcReichel\IGDBLaravel\Models\Game::with(['cover' => ['url', 'image_id']])->find($torrent->igdb); @endphp + @endif + @endif + +
+
+
+ + + {{ $torrent->seeders }} / + + {{ $torrent->leechers }} / + + {{ $torrent->times_completed }} +   + + {{ $torrent->getSize() }} +   + + {{ $torrent->resolution->name ?? 'No Res' }} + + + {{ $torrent->type->name }} +   + + {{ $torrent->category->name }} +   +
+
+
+ @if ($torrent->category->movie_meta || $torrent->category->tv_meta) + @lang('torrent.poster') + @endif + + @if ($torrent->category->game_meta && isset($meta) && $meta->cover->image_id && $meta->name) + ' + data-image='@lang('torrent.poster')' + class="torrent-poster-img-small show-poster" alt="@lang('torrent.poster')"> + @endif + + @if ($torrent->category->music_meta) + ' + class="torrent-poster-img-small show-poster" alt="@lang('torrent.poster')"> + @endif + + @if ($torrent->category->no_meta) + @if(file_exists(public_path().'/files/img/torrent-cover_'.$torrent->id.'.jpg')) + @lang('torrent.poster') + @else + ' + class="torrent-poster-img-small show-poster" alt="@lang('torrent.poster')"> + @endif + @endif +
+
+

+ + {{ $torrent->name }} + +

+ @if (isset($meta->genres) && $meta->genres->isNotEmpty()) + @foreach ($meta->genres as $genre) + + + {{ $genre->name }} + + + @endforeach + @endif +

+ @if($torrent->category->movie_meta || $torrent->category->tv_meta) + {{ $meta->overview ?? '' }} + @endif + + @if($torrent->category->game_meta) + {{ $meta->summary ?? '' }} + @endif +

+
+
+ +
+
+ @endforeach + @if (! $torrents->count()) +
+ @lang('common.no-result') +
+ @endif +
+
+ {{ $torrents->links() }} +
+
+
+
+ @lang('common.legend'): + + + + +
+
+
+
+ + + diff --git a/resources/views/torrent/cards.blade.php b/resources/views/torrent/cards.blade.php new file mode 100644 index 000000000..42fdca21f --- /dev/null +++ b/resources/views/torrent/cards.blade.php @@ -0,0 +1,23 @@ +@extends('layout.default') + +@section('title') + @lang('torrent.torrents') - {{ config('other.title') }} +@endsection + +@section('meta') + +@endsection + +@section('breadcrumb') +
  • + +
  • +@endsection + +@section('content') +
    + @livewire('torrent-card-search') +
    +@endsection diff --git a/routes/web.php b/routes/web.php index 0d0e680d2..5bb281e77 100755 --- a/routes/web.php +++ b/routes/web.php @@ -260,6 +260,7 @@ Route::group(['middleware' => 'language'], function () { Route::group(['prefix' => 'torrents'], function () { Route::get('/', [App\Http\Controllers\TorrentController::class, 'torrents'])->name('torrents'); + Route::get('/cards', [App\Http\Controllers\TorrentCardController::class, 'index'])->name('cards'); Route::get('/{id}{hash?}', [App\Http\Controllers\TorrentController::class, 'torrent'])->name('torrent'); Route::get('/{id}/peers', [App\Http\Controllers\TorrentController::class, 'peers'])->name('peers'); Route::get('/{id}/history', [App\Http\Controllers\TorrentController::class, 'history'])->name('history');