This commit is contained in:
DariusIII
2026-01-16 14:29:35 +01:00
14 changed files with 1243 additions and 410 deletions
+31 -13
View File
@@ -84,21 +84,39 @@ class BrowseController extends BasePageController
$catname = 'All';
} elseif ($category !== -1 && $grp === -1) {
$catname = $id;
// Determine the root category ID - either from the category's root_categories_id
// or the category itself if it IS a root category
$rootCategoryId = null;
$cdata = Category::find($category);
if ($cdata !== null) {
if ($cdata->root_categories_id === Category::GAME_ROOT) {
$covgroup = 'console';
} elseif ($cdata->root_categories_id === Category::MOVIE_ROOT) {
$covgroup = 'movies';
} elseif ($cdata->root_categories_id === Category::PC_ROOT) {
$covgroup = 'games';
} elseif ($cdata->root_categories_id === Category::MUSIC_ROOT) {
$covgroup = 'music';
} elseif ($cdata->root_categories_id === Category::BOOKS_ROOT) {
$covgroup = 'books';
} elseif ($cdata->root_categories_id === Category::TV_ROOT) {
$shows = true;
}
$rootCategoryId = $cdata->root_categories_id ?? $category;
} else {
// Category not found in categories table, might be a root category
// Check if it matches a known root category ID
$rootCategoryId = $category;
}
// Also check RootCategory table for parent categories (when $id is 'All')
if ($id === 'All' && $parentId !== null) {
$rootCategoryId = $parentId;
}
// Set covgroup based on root category
if ($rootCategoryId === Category::GAME_ROOT) {
$covgroup = 'console';
} elseif ($rootCategoryId === Category::MOVIE_ROOT) {
$covgroup = 'movies';
} elseif ($rootCategoryId === Category::PC_ROOT) {
$covgroup = 'games';
} elseif ($rootCategoryId === Category::MUSIC_ROOT) {
$covgroup = 'music';
} elseif ($rootCategoryId === Category::BOOKS_ROOT) {
$covgroup = 'books';
} elseif ($rootCategoryId === Category::XXX_ROOT) {
$covgroup = 'xxx';
} elseif ($rootCategoryId === Category::TV_ROOT) {
$shows = true;
}
} else {
$catname = $grp;
+26 -8
View File
@@ -168,18 +168,34 @@ class ConsoleService
Cache::put(md5($calcSql.$page), $consoles, $expiresAt);
}
$consoleIDs = $releaseIDs = false;
$consoleIDs = $releaseIDs = [];
if (\is_array($consoles['result'])) {
foreach ($consoles['result'] as $console => $id) {
$consoleIDs = [$id->id];
$releaseIDs = [$id->grp_release_id];
$consoleIDs[] = $id->id;
$releaseIDs[] = $id->grp_release_id;
}
}
$sql = sprintf(
'
"
SELECT
r.id, r.rarinnerfilecount, r.grabs, r.comments, r.totalpart, r.size, r.postdate, r.searchname, r.haspreview, r.passwordstatus, r.guid, rn.releases_id, g.name AS group_name, df.failed AS failed,
GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id,
GROUP_CONCAT(r.rarinnerfilecount ORDER BY r.postdate DESC SEPARATOR ',') AS grp_rarinnerfilecount,
GROUP_CONCAT(r.haspreview ORDER BY r.postdate DESC SEPARATOR ',') AS grp_haspreview,
GROUP_CONCAT(r.passwordstatus ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_password,
GROUP_CONCAT(r.guid ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_guid,
GROUP_CONCAT(rn.releases_id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_nfoid,
GROUP_CONCAT(g.name ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grpname,
GROUP_CONCAT(r.searchname ORDER BY r.postdate DESC SEPARATOR '#') AS grp_release_name,
GROUP_CONCAT(r.postdate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_postdate,
GROUP_CONCAT(r.adddate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_adddate,
GROUP_CONCAT(r.size ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_size,
GROUP_CONCAT(r.totalpart ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_totalparts,
GROUP_CONCAT(r.comments ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_comments,
GROUP_CONCAT(r.grabs ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grabs,
GROUP_CONCAT(df.failed ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_failed,
GROUP_CONCAT(cp.title, ' > ', c.title ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_catname,
GROUP_CONCAT(r.fromname ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_fromname,
con.*,
r.consoleinfo_id,
g.name AS group_name,
@@ -189,15 +205,17 @@ class ConsoleService
LEFT OUTER JOIN usenet_groups g ON g.id = r.groups_id
LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
LEFT OUTER JOIN categories c ON c.id = r.categories_id
LEFT OUTER JOIN root_categories cp ON cp.id = c.root_categories_id
INNER JOIN consoleinfo con ON con.id = r.consoleinfo_id
INNER JOIN genres ON con.genres_id = genres.id
WHERE con.id IN (%s)
AND r.id IN (%s)
%s
GROUP BY con.id
ORDER BY %s %s',
(\is_array($consoleIDs) ? implode(',', $consoleIDs) : -1),
(\is_array($releaseIDs) ? implode(',', $releaseIDs) : -1),
ORDER BY %s %s",
(! empty($consoleIDs) ? implode(',', $consoleIDs) : -1),
(! empty($releaseIDs) ? implode(',', $releaseIDs) : -1),
$catsrch,
$order[0],
$order[1]
+40 -5
View File
@@ -216,16 +216,51 @@ class GamesService
Cache::put(md5($gamesSql.$page), $games, $expiresAt);
}
$gameIDs = $releaseIDs = false;
$gameIDs = $releaseIDs = [];
if (is_array($games['result'])) {
foreach ($games['result'] as $game => $id) {
$gameIDs = [$id->id];
$releaseIDs = [$id->grp_release_id];
$gameIDs[] = $id->id;
$releaseIDs[] = $id->grp_release_id;
}
}
$returnSql =
'SELECT r.id, r.rarinnerfilecount, r.grabs, r.comments, r.totalpart, r.size, r.postdate, r.searchname, r.haspreview, r.passwordstatus, r.guid, g.name AS group_name, df.failed AS failed, gi.*, YEAR (gi.releasedate) as year, r.gamesinfo_id, rn.releases_id AS nfoid FROM releases r LEFT OUTER JOIN usenet_groups g ON g.id = r.groups_id LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id INNER JOIN gamesinfo gi ON gi.id = r.gamesinfo_id WHERE gi.id IN ('.(is_array($gameIDs) ? implode(',', $gameIDs) : -1).') AND r.id IN ('.(is_array($releaseIDs) ? implode(',', $releaseIDs) : -1).')'.$catsrch.' GROUP BY gi.id ORDER BY '.($order[0]).' '.($order[1]);
$returnSql = sprintf(
"SELECT
GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id,
GROUP_CONCAT(r.rarinnerfilecount ORDER BY r.postdate DESC SEPARATOR ',') AS grp_rarinnerfilecount,
GROUP_CONCAT(r.haspreview ORDER BY r.postdate DESC SEPARATOR ',') AS grp_haspreview,
GROUP_CONCAT(r.passwordstatus ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_password,
GROUP_CONCAT(r.guid ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_guid,
GROUP_CONCAT(rn.releases_id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_nfoid,
GROUP_CONCAT(g.name ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grpname,
GROUP_CONCAT(r.searchname ORDER BY r.postdate DESC SEPARATOR '#') AS grp_release_name,
GROUP_CONCAT(r.postdate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_postdate,
GROUP_CONCAT(r.adddate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_adddate,
GROUP_CONCAT(r.size ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_size,
GROUP_CONCAT(r.totalpart ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_totalparts,
GROUP_CONCAT(r.comments ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_comments,
GROUP_CONCAT(r.grabs ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grabs,
GROUP_CONCAT(df.failed ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_failed,
GROUP_CONCAT(cp.title, ' > ', c.title ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_catname,
gi.*, YEAR(gi.releasedate) as year, r.gamesinfo_id, rn.releases_id AS nfoid, g.name AS group_name
FROM releases r
LEFT OUTER JOIN usenet_groups g ON g.id = r.groups_id
LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
LEFT OUTER JOIN categories c ON c.id = r.categories_id
LEFT OUTER JOIN root_categories cp ON cp.id = c.root_categories_id
INNER JOIN gamesinfo gi ON gi.id = r.gamesinfo_id
WHERE gi.id IN (%s)
AND r.id IN (%s)
%s
GROUP BY gi.id
ORDER BY %s %s",
(! empty($gameIDs) ? implode(',', $gameIDs) : -1),
(! empty($releaseIDs) ? implode(',', $releaseIDs) : -1),
$catsrch,
$order[0],
$order[1]
);
$return = Cache::get(md5($returnSql.$page));
if ($return !== null) {
+20 -4
View File
@@ -152,21 +152,37 @@ class MusicService
}
$sql = sprintf(
'
"
SELECT
r.id, r.rarinnerfilecount, r.grabs, r.comments, r.totalpart, r.size, r.postdate, r.searchname, r.haspreview, r.passwordstatus, r.guid, df.failed AS failed,
GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id,
GROUP_CONCAT(r.rarinnerfilecount ORDER BY r.postdate DESC SEPARATOR ',') AS grp_rarinnerfilecount,
GROUP_CONCAT(r.haspreview ORDER BY r.postdate DESC SEPARATOR ',') AS grp_haspreview,
GROUP_CONCAT(r.passwordstatus ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_password,
GROUP_CONCAT(r.guid ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_guid,
GROUP_CONCAT(rn.releases_id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_nfoid,
GROUP_CONCAT(g.name ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grpname,
GROUP_CONCAT(r.searchname ORDER BY r.postdate DESC SEPARATOR '#') AS grp_release_name,
GROUP_CONCAT(r.postdate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_postdate,
GROUP_CONCAT(r.adddate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_adddate,
GROUP_CONCAT(r.size ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_size,
GROUP_CONCAT(r.totalpart ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_totalparts,
GROUP_CONCAT(r.comments ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_comments,
GROUP_CONCAT(r.grabs ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grabs,
GROUP_CONCAT(df.failed ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_failed,
GROUP_CONCAT(cp.title, ' > ', c.title ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_catname,
m.*,
r.musicinfo_id, r.haspreview,
g.name AS group_name,
rn.releases_id AS nfoid
FROM releases r
LEFT OUTER JOIN usenet_groups g ON g.id = r.groups_id
LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
LEFT OUTER JOIN categories c ON c.id = r.categories_id
LEFT OUTER JOIN root_categories cp ON cp.id = c.root_categories_id
INNER JOIN musicinfo m ON m.id = r.musicinfo_id
%s %s %s
GROUP BY m.id
ORDER BY %s %s',
ORDER BY %s %s",
! empty($musicIDs) ? 'WHERE m.id IN ('.implode(',', $musicIDs).')' : 'AND 1=1',
(! empty($releaseIDs)) ? 'AND r.id in ('.implode(',', $releaseIDs).')' : '',
$catsrch,
@@ -0,0 +1,190 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* This migration fixes the invitations table by:
* 1. Dropping the old FK_users_inv foreign key constraint
* 2. Renaming users_id to invited_by if needed
* 3. Adding missing columns for the new invitation system
*/
public function up(): void
{
// First, drop the old foreign key constraint if it exists
$this->dropForeignKeySafely('invitations', 'FK_users_inv');
Schema::table('invitations', function (Blueprint $table) {
// Rename users_id to invited_by if needed
if (Schema::hasColumn('invitations', 'users_id') && ! Schema::hasColumn('invitations', 'invited_by')) {
$table->renameColumn('users_id', 'invited_by');
}
});
// Now add missing columns in a separate Schema call (after rename)
Schema::table('invitations', function (Blueprint $table) {
// Drop the old guid column if it exists (replaced by token)
if (Schema::hasColumn('invitations', 'guid') && Schema::hasColumn('invitations', 'token')) {
$table->dropColumn('guid');
}
// Add token if it doesn't exist
if (! Schema::hasColumn('invitations', 'token')) {
$table->string('token', 64)->unique()->after('id');
}
// Add email if it doesn't exist
if (! Schema::hasColumn('invitations', 'email')) {
$table->string('email')->after('token');
}
// Add expires_at if it doesn't exist
if (! Schema::hasColumn('invitations', 'expires_at')) {
if (Schema::hasColumn('invitations', 'invited_by')) {
$table->timestamp('expires_at')->after('invited_by');
} else {
$table->timestamp('expires_at')->nullable();
}
}
// Add used_at if it doesn't exist
if (! Schema::hasColumn('invitations', 'used_at')) {
$table->timestamp('used_at')->nullable();
}
// Add used_by if it doesn't exist
if (! Schema::hasColumn('invitations', 'used_by')) {
$table->unsignedBigInteger('used_by')->nullable();
}
// Add is_active if it doesn't exist
if (! Schema::hasColumn('invitations', 'is_active')) {
$table->boolean('is_active')->default(true);
}
// Add metadata if it doesn't exist
if (! Schema::hasColumn('invitations', 'metadata')) {
$table->json('metadata')->nullable();
}
});
// Migrate data from guid to token if guid still exists and has data
if (Schema::hasColumn('invitations', 'guid') && Schema::hasColumn('invitations', 'token')) {
DB::table('invitations')
->whereNull('token')
->orWhere('token', '')
->update(['token' => DB::raw('guid')]);
// Now drop guid
Schema::table('invitations', function (Blueprint $table) {
$table->dropColumn('guid');
});
}
// Add indexes safely
$this->addIndexSafely('invitations', ['invited_by'], 'invitations_invited_by_index');
$this->addIndexSafely('invitations', ['used_by'], 'invitations_used_by_index');
$this->addIndexSafely('invitations', ['token', 'is_active'], 'invitations_token_is_active_index');
$this->addIndexSafely('invitations', ['email', 'is_active'], 'invitations_email_is_active_index');
$this->addIndexSafely('invitations', ['expires_at'], 'invitations_expires_at_index');
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Drop indexes first
$this->dropIndexSafely('invitations', 'invitations_invited_by_index');
$this->dropIndexSafely('invitations', 'invitations_used_by_index');
$this->dropIndexSafely('invitations', 'invitations_token_is_active_index');
$this->dropIndexSafely('invitations', 'invitations_email_is_active_index');
$this->dropIndexSafely('invitations', 'invitations_expires_at_index');
Schema::table('invitations', function (Blueprint $table) {
// Rename back to original column name
if (Schema::hasColumn('invitations', 'invited_by') && ! Schema::hasColumn('invitations', 'users_id')) {
$table->renameColumn('invited_by', 'users_id');
}
});
// Re-add the foreign key constraint
Schema::table('invitations', function (Blueprint $table) {
if (Schema::hasColumn('invitations', 'users_id')) {
$table->foreign('users_id', 'FK_users_inv')
->references('id')
->on('users')
->onDelete('cascade')
->onUpdate('cascade');
}
});
}
/**
* Safely drop a foreign key if it exists
*/
private function dropForeignKeySafely(string $table, string $foreignKeyName): void
{
try {
$foreignKeys = DB::select(
"SELECT CONSTRAINT_NAME FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY'
AND TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = ?
AND CONSTRAINT_NAME = ?",
[$table, $foreignKeyName]
);
if (! empty($foreignKeys)) {
Schema::table($table, function (Blueprint $table) use ($foreignKeyName) {
$table->dropForeign($foreignKeyName);
});
}
} catch (\Exception $e) {
// Foreign key might not exist, continue
report($e);
}
}
/**
* Safely add an index if it doesn't exist
*/
private function addIndexSafely(string $table, array $columns, string $indexName): void
{
try {
$indexes = DB::select("SHOW INDEX FROM `{$table}` WHERE Key_name = ?", [$indexName]);
if (empty($indexes)) {
Schema::table($table, function (Blueprint $table) use ($columns, $indexName) {
$table->index($columns, $indexName);
});
}
} catch (\Exception $e) {
// Index creation failed, but continue
report($e);
}
}
/**
* Safely drop an index if it exists
*/
private function dropIndexSafely(string $table, string $indexName): void
{
try {
$indexes = DB::select("SHOW INDEX FROM `{$table}` WHERE Key_name = ?", [$indexName]);
if (! empty($indexes)) {
Schema::table($table, function (Blueprint $table) use ($indexName) {
$table->dropIndex($indexName);
});
}
} catch (\Exception $e) {
// Index drop failed, but continue
report($e);
}
}
};
@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Remove the legacy users_id column since we now use invited_by
*/
public function up(): void
{
Schema::table('invitations', function (Blueprint $table) {
// Drop the old users_id column if it exists and invited_by exists
if (Schema::hasColumn('invitations', 'users_id') && Schema::hasColumn('invitations', 'invited_by')) {
$table->dropColumn('users_id');
}
// Drop guid column if it still exists (we use token now)
if (Schema::hasColumn('invitations', 'guid')) {
$table->dropColumn('guid');
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('invitations', function (Blueprint $table) {
if (! Schema::hasColumn('invitations', 'users_id')) {
$table->unsignedInteger('users_id')->nullable()->after('id');
}
});
}
};
+14 -5
View File
@@ -74,11 +74,20 @@
<!-- Results -->
@if(count($results) > 0)
<div class="mb-4 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800">
<i class="fa fa-book mr-2 text-blue-600"></i>
{{ $catname ?? 'All' }} Books
</h2>
<span class="text-sm text-gray-600">
<div class="flex items-center gap-4">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-book mr-2 text-blue-600"></i>
{{ $catname ?? 'All' }} Books
</h2>
<x-view-toggle
current-view="covers"
covgroup="books"
:category="$categorytitle ?? 'All'"
parentcat="Books"
:shows="false"
/>
</div>
<span class="text-sm text-gray-600 dark:text-gray-400">
{{ $results->total() }} results found
</span>
</div>
+17 -9
View File
@@ -56,13 +56,14 @@
</div>
@endif
@if(isset($covgroup) && $covgroup != '')
<div class="flex items-center gap-2 text-sm">
<span class="text-gray-600 dark:text-gray-400 dark:text-gray-400">View:</span>
<a href="{{ url('/' . $covgroup . '/' . $category) }}" class="text-blue-600 dark:text-blue-400 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 dark:hover:text-blue-300">Covers</a>
<span class="text-gray-400 dark:text-gray-500">|</span>
<span class="font-semibold text-gray-800 dark:text-gray-200 dark:text-gray-200">List</span>
</div>
@if(isset($covgroup) && $covgroup != '' || isset($shows) && $shows)
<x-view-toggle
current-view="list"
:covgroup="$covgroup ?? null"
:category="$category ?? null"
:parentcat="$parentcat ?? null"
:shows="$shows ?? false"
/>
@endif
<div class="flex flex-wrap items-center gap-2">
@@ -136,8 +137,15 @@
</td>
<td class="px-3 py-4">
<div class="flex items-start">
@if($result->cover ?? false)
<img src="{{ $result->cover }}" class="w-12 h-16 object-cover rounded mr-3" alt="Cover">
@php
$showThumbnails = request()->query('thumbs', '0') === '1';
$coverUrl = ($result->cover ?? false) ? $result->cover : ($showThumbnails ? getReleaseCover($result) : null);
$hasValidCover = $coverUrl && !str_contains($coverUrl, 'no-cover.png');
@endphp
@if($hasValidCover)
<a href="{{ url('/details/' . $result->guid) }}" class="flex-shrink-0">
<img src="{{ $coverUrl }}" class="w-12 h-16 object-cover rounded mr-3 shadow-sm hover:shadow-md transition" alt="Cover" loading="lazy">
</a>
@endif
<div class="flex-1">
<div class="flex items-center gap-2 flex-wrap">
@@ -0,0 +1,118 @@
@props([
'currentView' => 'list',
'covgroup' => null,
'category' => null,
'parentcat' => null,
'shows' => false
])
@php
// Build URLs for toggling views while preserving current query parameters
$currentParams = request()->query();
// Map covgroup to correct cover view route
$coverRouteMap = [
'movies' => 'Movies',
'console' => 'Console',
'games' => 'Games',
'music' => 'Audio',
'books' => 'Books',
'xxx' => 'XXX',
];
// Map covgroup to correct list browse parent category
$listParentMap = [
'movies' => 'Movies',
'console' => 'Console',
'games' => 'PC',
'music' => 'Audio',
'books' => 'Books',
'xxx' => 'XXX',
];
// Parent category IDs - these should not be appended to URLs
$parentCategoryIds = [
1000, // Console/Game
2000, // Movies
3000, // Audio/Music
4000, // PC
5000, // TV
6000, // XXX
7000, // Books
8000, // Other
];
// Check if category is a parent category ID (should not be in URL path)
$isParentCategory = is_numeric($category) && in_array((int)$category, $parentCategoryIds);
// Build cover view URL
if ($covgroup && isset($coverRouteMap[$covgroup])) {
$coverRoute = $coverRouteMap[$covgroup];
// Only append category if it's not a parent category ID and not 'All' or -1
$catParam = ($category && $category !== -1 && $category !== 'All' && !$isParentCategory) ? $category : '';
$coverUrl = url('/' . $coverRoute . ($catParam ? '/' . $catParam : ''));
} elseif ($shows) {
$coverUrl = route('series');
} else {
$coverUrl = '#';
}
// Build list view URL - preserve query params except view and thumbs for clean URL
$listParams = $currentParams;
unset($listParams['view']);
// Determine the correct parent category for list view
$listParent = $parentcat;
if ($covgroup && isset($listParentMap[$covgroup])) {
$listParent = $listParentMap[$covgroup];
}
// Only append category to list URL if it's not a parent category ID
if ($listParent && $category && $category !== -1 && $category !== 'All' && !$isParentCategory) {
$listUrl = url('/browse/' . $listParent . '/' . $category);
} elseif ($listParent) {
$listUrl = url('/browse/' . $listParent);
} else {
$listUrl = request()->url();
}
// Add query string if there are params
if (!empty($listParams)) {
$listUrl .= '?' . http_build_query($listParams);
}
// Build thumbnail toggle URL for list view
$thumbParams = $currentParams;
$showThumbnails = request()->query('thumbs', '0') === '1';
$thumbParams['thumbs'] = $showThumbnails ? '0' : '1';
$thumbUrl = request()->url() . '?' . http_build_query($thumbParams);
@endphp
<div class="flex items-center gap-2 text-sm">
<span class="text-gray-600 dark:text-gray-400">View:</span>
@if($currentView === 'covers')
{{-- Currently in cover view --}}
<span class="font-semibold text-gray-800 dark:text-gray-200">Covers</span>
<span class="text-gray-400 dark:text-gray-500">|</span>
<a href="{{ $listUrl }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">List</a>
@else
{{-- Currently in list view --}}
@if($covgroup || $shows)
<a href="{{ $coverUrl }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">Covers</a>
<span class="text-gray-400 dark:text-gray-500">|</span>
@endif
<span class="font-semibold text-gray-800 dark:text-gray-200">List</span>
{{-- Thumbnail toggle in list view --}}
@if($covgroup || $shows)
<span class="text-gray-400 dark:text-gray-500 ml-2">|</span>
<a href="{{ $thumbUrl }}"
class="inline-flex items-center gap-1 {{ $showThumbnails ? 'text-green-600 dark:text-green-400' : 'text-blue-600 dark:text-blue-400' }} hover:text-blue-800 dark:hover:text-blue-300"
title="{{ $showThumbnails ? 'Hide thumbnails' : 'Show thumbnails' }}">
<i class="fas fa-image text-xs"></i>
<span>{{ $showThumbnails ? 'Hide Thumbs' : 'Show Thumbs' }}</span>
</a>
@endif
@endif
</div>
+187 -160
View File
@@ -42,13 +42,16 @@
<div class="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4">
<!-- Left Section -->
<div class="flex flex-col sm:flex-row items-start sm:items-center gap-4">
<div class="text-sm">
<span class="text-gray-600">View: <strong>Covers</strong> |
<a href="{{ url('/browse/Console/' . ($categorytitle ?? '')) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-800">List</a></span>
</div>
<x-view-toggle
current-view="covers"
covgroup="console"
:category="$categorytitle ?? 'All'"
parentcat="Console"
:shows="false"
/>
<div class="flex items-center gap-2">
<small class="text-gray-600">With Selected:</small>
<small class="text-gray-600 dark:text-gray-400">With Selected:</small>
<div class="flex gap-1">
<button type="button" class="nzb_multi_operations_download px-3 py-1 bg-green-600 dark:bg-green-700 text-white rounded hover:bg-green-700 dark:hover:bg-green-800 transition text-sm" title="Download NZBs">
<i class="fa fa-cloud-download"></i>
@@ -73,170 +76,192 @@
<!-- Results Grid -->
<div class="px-6 py-4">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
@foreach($resultsadd as $result)
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div class="p-4">
<div class="grid grid-cols-12 gap-4">
<!-- Console Cover Column -->
<div class="col-span-12 sm:col-span-4">
<div class="relative mb-3">
<a href="{{ url('/details/' . $result->guid) }}">
<img class="w-full rounded shadow"
src="{{ getReleaseCover($result) }}"
alt="{{ $result->title }}"/>
@if(!empty($result->failed))
<div class="absolute top-2 right-2">
<span class="px-2 py-1 bg-red-600 dark:bg-red-700 text-white text-xs rounded-full">
<i class="fa fa-exclamation-circle mr-1"></i>Failed
</span>
</div>
@php
// Extract grouped release data
$releaseGuids = isset($result->grp_release_guid) ? explode(',', $result->grp_release_guid) : [];
$releaseNames = isset($result->grp_release_name) ? explode('#', $result->grp_release_name) : [];
$releaseSizes = isset($result->grp_release_size) ? explode(',', $result->grp_release_size) : [];
$releasePostDates = isset($result->grp_release_postdate) ? explode(',', $result->grp_release_postdate) : [];
$releaseAddDates = isset($result->grp_release_adddate) ? explode(',', $result->grp_release_adddate) : [];
$releaseGrabs = isset($result->grp_release_grabs) ? explode(',', $result->grp_release_grabs) : [];
$releaseComments = isset($result->grp_release_comments) ? explode(',', $result->grp_release_comments) : [];
$releaseNfoIds = isset($result->grp_release_nfoid) ? explode(',', $result->grp_release_nfoid) : [];
$releaseHasPreview = isset($result->grp_haspreview) ? explode(',', $result->grp_haspreview) : [];
$releaseCategories = isset($result->grp_release_catname) ? explode(',', $result->grp_release_catname) : [];
$releaseFromNames = isset($result->grp_release_fromname) ? explode(',', $result->grp_release_fromname) : [];
$failedCounts = isset($result->grp_release_failed) ? array_filter(explode(',', $result->grp_release_failed)) : [];
$totalFailed = array_sum($failedCounts);
// Limit to maximum 2 releases displayed
$maxReleases = 2;
$totalReleases = count($releaseGuids);
$releaseGuids = array_slice($releaseGuids, 0, $maxReleases);
$releaseNames = array_slice($releaseNames, 0, $maxReleases);
$releaseSizes = array_slice($releaseSizes, 0, $maxReleases);
$releasePostDates = array_slice($releasePostDates, 0, $maxReleases);
$releaseAddDates = array_slice($releaseAddDates, 0, $maxReleases);
$releaseGrabs = array_slice($releaseGrabs, 0, $maxReleases);
$releaseComments = array_slice($releaseComments, 0, $maxReleases);
$releaseNfoIds = array_slice($releaseNfoIds, 0, $maxReleases);
$releaseHasPreview = array_slice($releaseHasPreview, 0, $maxReleases);
$releaseCategories = array_slice($releaseCategories, 0, $maxReleases);
$releaseFromNames = array_slice($releaseFromNames, 0, $maxReleases);
$guid = $releaseGuids[0] ?? null;
@endphp
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow">
<div class="flex flex-row">
<!-- Console Cover -->
<div class="flex-shrink-0">
@if($guid)
<a href="{{ url('/details/' . $guid) }}" class="block relative">
<img class="w-32 h-48 object-cover"
src="{{ getReleaseCover($result) }}"
alt="{{ $result->title }}"
onerror="this.onerror=null;this.src='{{ url('/images/no-cover.png') }}';">
@if($totalFailed > 0)
<div class="absolute top-2 right-2">
<span class="px-2 py-1 bg-red-600 dark:bg-red-700 text-white text-xs rounded-full">
<i class="fa fa-exclamation-circle mr-1"></i>{{ $totalFailed }} Failed
</span>
</div>
@endif
</a>
@else
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-gamepad text-gray-400 text-2xl"></i>
</div>
@endif
</div>
<!-- Console Details -->
<div class="flex-1 p-4">
<div class="flex justify-between items-start mb-2">
<div class="flex-1">
<h3 class="text-xl font-bold text-gray-900 dark:text-gray-100">{{ $result->title }}</h3>
<div class="flex items-center gap-4 mt-1 text-sm text-gray-600 dark:text-gray-400">
@if(isset($result->releasedate) && $result->releasedate != "")
<span><i class="fas fa-calendar mr-1"></i> {{ \Carbon\Carbon::parse($result->releasedate)->format('Y') }}</span>
@endif
</a>
</div>
@if(isset($result->platform) && $result->platform != "")
<span><i class="fas fa-gamepad mr-1"></i> {{ $result->platform }}</span>
@endif
</div>
<!-- External Links -->
<div class="flex flex-wrap gap-1 mb-2">
@if(!empty($result->url))
<a target="_blank" href="{{ $site['dereferrer_link'] }}{{ $result->url }}"
title="View Game page" class="px-2 py-1 bg-gray-600 text-white text-xs rounded hover:bg-gray-700">
<i class="fa fa-shopping-cart mr-1"></i>Amazon
</a>
@endif
@if($result->nfoid > 0)
<a href="{{ url('/nfo/' . $result->guid) }}" data-guid="{{ $result->guid }}"
title="View NFO" class="modal_nfo px-2 py-1 bg-gray-600 text-white text-xs rounded hover:bg-gray-700">
<i class="fa fa-file-text mr-1"></i>NFO
</a>
@endif
<a class="px-2 py-1 bg-gray-600 text-white text-xs rounded hover:bg-gray-700"
href="{{ url('/browse/group?g=' . $result->group_name) }}"
title="Browse releases in {{ str_replace('alt.binaries', 'a.b', $result->group_name) }}">
<i class="fa fa-users mr-1"></i>Group
</a>
</div>
</div>
<!-- Console Info Column -->
<div class="col-span-12 sm:col-span-8">
<!-- Console Title -->
<h5 class="text-lg font-semibold mb-2">
<a class="text-gray-800 dark:text-gray-200 hover:text-blue-600" href="{{ url('/details/' . $result->guid) }}">
{{ $result->title }}
</a>
</h5>
<!-- Console Details -->
<div class="mb-3 text-sm space-y-1">
@if(isset($result->genre) && $result->genre != "")
<div class="flex items-start">
<i class="fa fa-tags text-gray-400 mr-2 mt-1"></i>
<div class="text-xs text-gray-600 dark:text-gray-400 mt-2 space-y-1">
@if(isset($result->genre) && $result->genre != "")
<div class="flex flex-wrap gap-1">
<strong>Genre:</strong>
@foreach(explode(', ', $result->genre) as $genre)
<span class="px-2 py-0.5 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded text-xs">{{ $genre }}</span>
@endforeach
</div>
</div>
@endif
@if(isset($result->esrb) && $result->esrb != "")
<div class="flex items-center text-gray-600">
<i class="fa fa-shield text-gray-400 mr-2"></i>
<span><strong>Rating:</strong> {{ $result->esrb }}</span>
</div>
@endif
@if(isset($result->publisher) && $result->publisher != "")
<div class="flex items-center text-gray-600">
<i class="fa fa-building text-gray-400 mr-2"></i>
<span><strong>Publisher:</strong> {{ $result->publisher }}</span>
</div>
@endif
@if(isset($result->releasedate) && $result->releasedate != "")
<div class="flex items-center text-gray-600">
<i class="fa fa-calendar text-gray-400 mr-2"></i>
<span><strong>Released:</strong> {{ \Carbon\Carbon::parse($result->releasedate)->format('M d, Y') }}</span>
</div>
@endif
@if(isset($result->review) && $result->review != "")
<div class="text-gray-600 dark:text-gray-400 line-clamp-3">
<i class="fa fa-quote-left text-gray-400 mr-2"></i>{{ $result->review }}
</div>
@endif
</div>
<!-- Release Info Card -->
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-3 border border-gray-200">
<div class="flex justify-between items-start mb-2">
<label class="inline-flex items-center">
<input type="checkbox" class="form-checkbox h-4 w-4 text-blue-600" value="{{ $result->guid }}" id="chksingle"/>
</label>
<div class="flex items-center text-sm">
<i class="fa fa-hdd-o text-gray-400 mr-2"></i>
<span class="px-2 py-1 bg-gray-600 text-white text-xs rounded">{{ formatBytes($result->size) }}</span>
</div>
</div>
@if(!empty($result->adddate))
<div class="flex items-center text-gray-500 text-sm mb-3">
<i class="fa fa-clock-o mr-2"></i>
<span>Added {{ userDateDiffForHumans($result->adddate) }}</span>
</div>
@endif
<div class="flex flex-wrap gap-2 text-xs mb-3">
@if(!empty($result->group_name))
<span class="inline-flex items-center px-2 py-0.5 rounded bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300">
<i class="fas fa-users mr-1"></i> {{ $result->group_name }}
</span>
@endif
@if(!empty($result->postdate))
<span>
<i class="fas fa-calendar mr-1"></i> Posted: {{ userDate($result->postdate, 'M d, Y H:i') }}
</span>
@if(isset($result->publisher) && $result->publisher != "")
<div><strong>Publisher:</strong> {{ $result->publisher }}</div>
@endif
@if(!empty($result->fromname))
<span class="inline-flex items-center px-2 py-0.5 rounded bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200 font-mono">
<i class="fas fa-user mr-1"></i> {{ $result->fromname }}
</span>
@if(isset($result->esrb) && $result->esrb != "")
<div><strong>Rating:</strong> {{ $result->esrb }}</div>
@endif
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap gap-2" id="guid{{ $result->guid }}">
<a class="px-3 py-1.5 bg-blue-600 dark:bg-blue-700 text-white rounded hover:bg-blue-700 dark:hover:bg-blue-800 text-sm inline-flex items-center"
title="Download NZB"
href="{{ url('/getnzb?id=' . $result->guid) }}">
<i class="fa fa-cloud-download mr-1"></i>
<span class="px-1.5 py-0.5 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-xs rounded ml-1">{{ $result->grabs }}</span>
</a>
<a class="px-3 py-1.5 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-300 text-sm inline-flex items-center"
href="{{ url('/details/' . $result->guid . '#comments') }}">
<i class="fa fa-comment-o mr-1"></i>
<span class="px-1.5 py-0.5 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-xs rounded ml-1">{{ $result->comments }}</span>
</a>
<a href="#" class="add-to-cart px-3 py-1.5 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-300 text-sm"
data-guid="{{ $result->guid }}"
title="Send to my download basket">
<i class="icon_cart fa fa-shopping-basket"></i>
</a>
@if(!empty($result->failed))
<span class="px-3 py-1.5 bg-red-100 text-red-700 rounded text-sm">
<i class="fa fa-exclamation-triangle mr-1"></i>
{{ $result->failed }} Failed
</span>
@endif
</div>
<!-- External Links -->
@if(!empty($result->url))
<div class="mt-2">
<a target="_blank" href="{{ $site['dereferrer_link'] }}{{ $result->url }}"
title="View Game page" class="inline-flex items-center px-2 py-1 bg-gray-600 text-white text-xs rounded hover:bg-gray-700">
<i class="fa fa-shopping-cart mr-1"></i>Amazon
</a>
</div>
@endif
</div>
</div>
<!-- Release Information -->
@if(!empty($releaseGuids[0]))
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
Available Releases
@if($totalReleases > $maxReleases)
<span class="text-xs font-normal text-gray-500">(Showing {{ $maxReleases }} of {{ $totalReleases }})</span>
@endif
</h4>
<div class="space-y-2">
@foreach($releaseNames as $index => $releaseName)
@if($releaseName && isset($releaseGuids[$index]))
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-2 border border-gray-200 dark:border-gray-700">
<div class="space-y-2">
<div class="flex items-start justify-between gap-2">
<!-- Release Name -->
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="text-sm text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 font-medium block break-all flex-1" title="{{ $releaseName }}">
{{ $releaseName }}
</a>
<label class="inline-flex items-center flex-shrink-0">
<input type="checkbox" class="form-checkbox h-4 w-4 text-blue-600" value="{{ $releaseGuids[$index] }}" id="chksingle"/>
</label>
</div>
<!-- Info Badges -->
<div class="flex flex-wrap items-center gap-1.5">
@if(isset($releaseSizes[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-hdd mr-1"></i>{{ number_format($releaseSizes[$index] / 1073741824, 2) }} GB
</span>
@endif
@if(isset($releasePostDates[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-calendar-alt mr-1"></i>{{ date('M d, Y H:i', strtotime($releasePostDates[$index])) }}
</span>
@endif
@if(isset($releaseAddDates[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-plus-circle mr-1"></i>{{ userDateDiffForHumans($releaseAddDates[$index]) }}
</span>
@endif
@if(isset($releaseNfoIds[$index]) && !empty($releaseNfoIds[$index]))
<button type="button"
class="nfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 hover:bg-yellow-200 dark:hover:bg-yellow-800 transition cursor-pointer"
data-guid="{{ $releaseGuids[$index] }}"
title="View NFO file">
<i class="fas fa-file-alt mr-1"></i> NFO
</button>
@endif
@if(isset($releaseFromNames[$index]) && !empty($releaseFromNames[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200" title="Poster/Uploader">
<i class="fas fa-user mr-1"></i> {{ $releaseFromNames[$index] }}
</span>
@endif
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap items-center gap-1.5">
<a href="{{ url('/getnzb/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-600 dark:bg-green-700 text-white hover:bg-green-700 dark:hover:bg-green-800 transition">
<i class="fas fa-download mr-1"></i> Download
@if(isset($releaseGrabs[$index]) && $releaseGrabs[$index] > 0)
<span class="ml-1 px-1 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-xs rounded">{{ $releaseGrabs[$index] }}</span>
@endif
</a>
<button class="add-to-cart inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-600 dark:bg-blue-700 text-white hover:bg-blue-700 dark:hover:bg-blue-800 transition" data-guid="{{ $releaseGuids[$index] }}">
<i class="fas fa-shopping-cart mr-1"></i> Cart
</button>
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-600 dark:bg-gray-700 text-white hover:bg-gray-700 dark:hover:bg-gray-800 transition">
<i class="fas fa-info-circle mr-1"></i> Details
@if(isset($releaseComments[$index]) && $releaseComments[$index] > 0)
<span class="ml-1 px-1 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-xs rounded">{{ $releaseComments[$index] }}</span>
@endif
</a>
</div>
</div>
</div>
@endif
@endforeach
</div>
</div>
@endif
</div>
</div>
</div>
@@ -245,17 +270,17 @@
</div>
<!-- Footer -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200">
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
<div class="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4">
<!-- Left Section -->
<div class="flex flex-col sm:flex-row items-start sm:items-center gap-4">
<div class="text-sm">
<span class="text-gray-600">View: <strong>Covers</strong> |
<div class="text-sm text-gray-600 dark:text-gray-400">
<span>View: <strong>Covers</strong> |
<a href="{{ url('/browse/Console/' . ($categorytitle ?? '')) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-800">List</a></span>
</div>
<div class="flex items-center gap-2">
<small class="text-gray-600">With Selected:</small>
<small class="text-gray-600 dark:text-gray-400">With Selected:</small>
<div class="flex gap-1">
<button type="button" class="nzb_multi_operations_download px-3 py-1 bg-green-600 dark:bg-green-700 text-white rounded hover:bg-green-700 dark:hover:bg-green-800 transition text-sm" title="Download NZBs">
<i class="fa fa-cloud-download"></i>
@@ -280,16 +305,18 @@
</form>
@else
<div class="px-6 py-8">
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 text-blue-800">
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700 rounded-lg p-4 text-blue-800 dark:text-blue-200">
<i class="fa fa-info-circle mr-2"></i>
No console releases with covers available!
</div>
</div>
@endif
</div>
<!-- NFO Modal -->
@include('partials.nfo-modal')
@endsection
@push('scripts')
@include('partials.cart-script')
@endpush
+178 -60
View File
@@ -27,7 +27,7 @@
<label for="category" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Category</label>
<select id="category"
name="t"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500">
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
<option value="">All Categories</option>
@foreach($catlist ?? [] as $cat)
<option value="{{ $cat['id'] }}" {{ ($category ?? '') == $cat['id'] ? 'selected' : '' }}>
@@ -45,7 +45,7 @@
name="title"
value="{{ $title ?? '' }}"
placeholder="Search by title"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500">
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
</div>
<!-- Genre Filter -->
@@ -53,7 +53,7 @@
<label for="genre" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Genre</label>
<select id="genre"
name="genre"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500">
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
<option value="">All Genres</option>
@foreach($genres ?? [] as $g)
<option value="{{ $g->id }}" {{ ($genre ?? '') == $g->id ? 'selected' : '' }}>
@@ -68,7 +68,7 @@
<label for="year" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Year</label>
<select id="year"
name="year"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500">
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
<option value="">All Years</option>
@foreach($years ?? [] as $y)
<option value="{{ $y }}" {{ ($year ?? '') == $y ? 'selected' : '' }}>
@@ -93,67 +93,182 @@
<!-- Results -->
@if(count($results) > 0)
<div class="mb-4 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800">
<i class="fa fa-gamepad mr-2 text-blue-600"></i>
{{ $catname ?? 'All' }} Games
</h2>
<span class="text-sm text-gray-600">
<div class="flex items-center gap-4">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-gamepad mr-2 text-blue-600"></i>
{{ $catname ?? 'All' }} Games
</h2>
<x-view-toggle
current-view="covers"
covgroup="games"
:category="$catname ?? 'All'"
parentcat="PC"
:shows="false"
/>
</div>
<span class="text-sm text-gray-600 dark:text-gray-400">
{{ $results->total() }} results found
</span>
</div>
<!-- Games Grid -->
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 mb-6">
<!-- Games Grid - Card Layout with Multiple Releases -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6">
@foreach($results as $result)
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow duration-200">
<a href="{{ url('/details/' . $result->guid) }}" class="block relative">
@if(!empty($result->cover))
<img src="{{ url('/covers/games/' . $result->cover) }}"
alt="{{ $result->title ?? $result->searchname }}"
class="w-full h-48 object-cover"
data-fallback-src="{{ url('/images/no-cover.png') }}">
@else
<div class="w-full h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fa fa-gamepad text-4xl text-gray-400"></i>
</div>
@endif
@if(!empty($result->failed) && $result->failed > 0)
<div class="absolute top-2 right-2">
<span class="px-2 py-1 bg-red-600 dark:bg-red-700 text-white text-xs rounded-full shadow-lg" title="{{ $result->failed }} user(s) reported download failure">
<i class="fa fa-exclamation-triangle mr-1"></i>Failed
</span>
</div>
@endif
<div class="p-3">
<h3 class="font-semibold text-sm text-gray-800 dark:text-gray-200 break-words break-all" title="{{ $result->title ?? $result->searchname }}">
{{ $result->title ?? $result->searchname }}
</h3>
@if(!empty($result->publisher))
<p class="text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ $result->publisher }}">
{{ $result->publisher }}
</p>
@endif
@if(!empty($result->releasedate))
<p class="text-xs text-gray-500 mt-1">{{ date('Y', strtotime($result->releasedate)) }}</p>
@elseif(!empty($result->year))
<p class="text-xs text-gray-500 mt-1">{{ $result->year }}</p>
@endif
@if(!empty($result->platform))
<p class="text-xs text-blue-600 dark:text-blue-400 mt-1 truncate">{{ $result->platform }}</p>
@php
// Extract grouped release data
$releaseGuids = isset($result->grp_release_guid) ? explode(',', $result->grp_release_guid) : [];
$releaseNames = isset($result->grp_release_name) ? explode('#', $result->grp_release_name) : [];
$releaseSizes = isset($result->grp_release_size) ? explode(',', $result->grp_release_size) : [];
$releasePostDates = isset($result->grp_release_postdate) ? explode(',', $result->grp_release_postdate) : [];
$releaseAddDates = isset($result->grp_release_adddate) ? explode(',', $result->grp_release_adddate) : [];
$releaseGrabs = isset($result->grp_release_grabs) ? explode(',', $result->grp_release_grabs) : [];
$releaseNfoIds = isset($result->grp_release_nfoid) ? explode(',', $result->grp_release_nfoid) : [];
$releaseHasPreview = isset($result->grp_haspreview) ? explode(',', $result->grp_haspreview) : [];
$releaseCategories = isset($result->grp_release_catname) ? explode(',', $result->grp_release_catname) : [];
$failedCounts = isset($result->grp_release_failed) ? array_filter(explode(',', $result->grp_release_failed)) : [];
$totalFailed = array_sum($failedCounts);
// Limit to maximum 2 releases displayed
$maxReleases = 2;
$totalReleases = count($releaseGuids);
$releaseGuids = array_slice($releaseGuids, 0, $maxReleases);
$releaseNames = array_slice($releaseNames, 0, $maxReleases);
$releaseSizes = array_slice($releaseSizes, 0, $maxReleases);
$releasePostDates = array_slice($releasePostDates, 0, $maxReleases);
$releaseAddDates = array_slice($releaseAddDates, 0, $maxReleases);
$releaseGrabs = array_slice($releaseGrabs, 0, $maxReleases);
$releaseNfoIds = array_slice($releaseNfoIds, 0, $maxReleases);
$releaseHasPreview = array_slice($releaseHasPreview, 0, $maxReleases);
$releaseCategories = array_slice($releaseCategories, 0, $maxReleases);
$guid = $releaseGuids[0] ?? null;
@endphp
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow">
<div class="flex flex-row">
<!-- Game Cover -->
<div class="flex-shrink-0">
@if($guid)
<a href="{{ url('/details/' . $guid) }}" class="block">
@if(!empty($result->cover))
<img src="{{ url('/covers/games/' . $result->cover) }}"
alt="{{ $result->title ?? $result->searchname }}"
class="w-32 h-48 object-cover"
onerror="this.onerror=null;this.src='{{ url('/images/no-cover.png') }}';">
@else
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-gamepad text-gray-400 text-2xl"></i>
</div>
@endif
</a>
@else
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-gamepad text-gray-400 text-2xl"></i>
</div>
@endif
</div>
<!-- Game Details -->
<div class="flex-1 p-4">
<div class="flex justify-between items-start mb-2">
<div class="flex-1">
<h3 class="text-xl font-bold text-gray-900 dark:text-gray-100">{{ $result->title ?? $result->searchname }}</h3>
@if($totalFailed > 0)
<div class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-red-100 text-red-800 border border-red-200 mt-1">
<i class="fas fa-exclamation-triangle mr-1"></i>
<span>{{ $totalFailed }} failed report{{ $totalFailed > 1 ? 's' : '' }}</span>
</div>
@endif
<div class="flex items-center gap-4 mt-1 text-sm text-gray-600 dark:text-gray-400">
@if(!empty($result->releasedate))
<span><i class="fas fa-calendar mr-1"></i> {{ date('Y', strtotime($result->releasedate)) }}</span>
@elseif(!empty($result->year))
<span><i class="fas fa-calendar mr-1"></i> {{ $result->year }}</span>
@endif
@if(!empty($result->platform))
<span><i class="fas fa-desktop mr-1"></i> {{ $result->platform }}</span>
@endif
</div>
<div class="text-xs text-gray-600 dark:text-gray-400 mt-2 space-y-1">
@if(!empty($result->publisher))
<div><strong>Publisher:</strong> {{ $result->publisher }}</div>
@endif
@if(!empty($result->genre))
<div><strong>Genre:</strong> {{ $result->genre }}</div>
@endif
</div>
</div>
</div>
<!-- Release Information -->
@if(!empty($releaseGuids[0]))
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
Available Releases
@if($totalReleases > $maxReleases)
<span class="text-xs font-normal text-gray-500">(Showing {{ $maxReleases }} of {{ $totalReleases }})</span>
@endif
</h4>
<div class="space-y-2">
@foreach($releaseNames as $index => $releaseName)
@if($releaseName && isset($releaseGuids[$index]))
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-2 border border-gray-200 dark:border-gray-700">
<div class="space-y-2">
<!-- Release Name -->
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="text-sm text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 font-medium block break-all" title="{{ $releaseName }}">
{{ $releaseName }}
</a>
<!-- Info Badges -->
<div class="flex flex-wrap items-center gap-1.5">
@if(isset($releaseSizes[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-hdd mr-1"></i>{{ number_format($releaseSizes[$index] / 1073741824, 2) }} GB
</span>
@endif
@if(isset($releasePostDates[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-calendar-alt mr-1"></i>{{ date('M d, Y H:i', strtotime($releasePostDates[$index])) }}
</span>
@endif
@if(isset($releaseCategories[$index]) && !empty($releaseCategories[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
<i class="fas fa-folder mr-1"></i>{{ $releaseCategories[$index] }}
</span>
@endif
@if(isset($releaseNfoIds[$index]) && !empty($releaseNfoIds[$index]))
<button type="button"
class="nfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 hover:bg-yellow-200 dark:hover:bg-yellow-800 transition cursor-pointer"
data-guid="{{ $releaseGuids[$index] }}"
title="View NFO file">
<i class="fas fa-file-alt mr-1"></i> NFO
</button>
@endif
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap items-center gap-1.5">
<a href="{{ url('/getnzb/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-600 dark:bg-green-700 text-white hover:bg-green-700 dark:hover:bg-green-800 transition">
<i class="fas fa-download mr-1"></i> Download
</a>
<button class="add-to-cart inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-600 dark:bg-blue-700 text-white hover:bg-blue-700 dark:hover:bg-blue-800 transition" data-guid="{{ $releaseGuids[$index] }}">
<i class="fas fa-shopping-cart mr-1"></i> Cart
</button>
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-600 dark:bg-gray-700 text-white hover:bg-gray-700 dark:hover:bg-gray-800 transition">
<i class="fas fa-info-circle mr-1"></i> Details
</a>
</div>
</div>
</div>
@endif
@endforeach
</div>
</div>
@endif
</div>
</a>
<div class="px-3 pb-3 flex gap-1">
<a href="{{ url('/getnzb?id=' . $result->guid) }}"
class="flex-1 px-2 py-1 bg-green-600 dark:bg-green-700 text-white text-xs rounded hover:bg-green-700 dark:hover:bg-green-800 text-center"
title="Download NZB">
<i class="fa fa-download"></i>
</a>
<a href="{{ url('/details/' . $result->guid) }}"
class="flex-1 px-2 py-1 bg-blue-600 dark:bg-blue-700 text-white text-xs rounded hover:bg-blue-700 dark:hover:bg-blue-800 text-center"
title="View Details">
<i class="fa fa-info-circle"></i>
</a>
</div>
</div>
@endforeach
@@ -165,8 +280,8 @@
</div>
@else
<!-- No Results -->
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-8 text-center">
<i class="fa fa-gamepad text-yellow-600 text-5xl mb-4"></i>
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-700 rounded-lg p-8 text-center">
<i class="fa fa-gamepad text-yellow-600 dark:text-yellow-500 text-5xl mb-4"></i>
<h3 class="text-xl font-semibold text-gray-800 dark:text-gray-200 mb-2">No games found</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">Try adjusting your search filters or browse all games.</p>
<a href="{{ url('/Games') }}" class="inline-flex items-center px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
@@ -176,5 +291,8 @@
@endif
</div>
</div>
<!-- NFO Modal -->
@include('partials.nfo-modal')
@endsection
+10 -1
View File
@@ -24,7 +24,16 @@
<!-- Movies Filter Section -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">Filter Movies</h2>
<div class="flex items-center gap-4">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">Filter Movies</h2>
<x-view-toggle
current-view="covers"
covgroup="movies"
category="All"
parentcat="Movies"
:shows="false"
/>
</div>
<div class="flex gap-2">
<!-- Layout Toggle Button -->
<button id="layoutToggle" class="inline-flex items-center px-4 py-2 bg-gray-600 dark:bg-gray-700 text-white rounded-lg hover:bg-gray-700 dark:hover:bg-gray-800 transition shadow-md" title="Toggle layout">
+167 -49
View File
@@ -90,63 +90,178 @@
<!-- Results -->
@if(count($results) > 0)
<div class="mb-4 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-music mr-2 text-blue-600 dark:text-blue-400"></i>
{{ $catname ?? 'All' }} Albums
</h2>
<div class="flex items-center gap-4">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-music mr-2 text-blue-600 dark:text-blue-400"></i>
{{ $catname ?? 'All' }} Albums
</h2>
<x-view-toggle
current-view="covers"
covgroup="music"
:category="$categorytitle ?? 'All'"
parentcat="Audio"
:shows="false"
/>
</div>
<span class="text-sm text-gray-600 dark:text-gray-400">
{{ $results->total() }} results found
</span>
</div>
<!-- Album Grid -->
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 mb-6">
<!-- Album Grid - Card Layout with Multiple Releases -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6">
@foreach($resultsadd as $result)
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow duration-200">
<a href="{{ url('/details/' . $result->guid) }}" class="block relative">
@if(!empty($result->cover))
<img src="{{ url('/covers/music/' . $result->cover) }}"
alt="{{ $result->artist ?? '' }} - {{ $result->title ?? '' }}"
class="w-full h-48 object-cover"
data-fallback-src="{{ url('/images/no-cover.png') }}">
@else
<div class="w-full h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fa fa-music text-4xl text-gray-400"></i>
</div>
@endif
@if(!empty($result->failed) && $result->failed > 0)
<div class="absolute top-2 right-2">
<span class="px-2 py-1 bg-red-600 dark:bg-red-700 text-white text-xs rounded-full shadow-lg" title="{{ $result->failed }} user(s) reported download failure">
<i class="fa fa-exclamation-triangle mr-1"></i>Failed
</span>
</div>
@endif
<div class="p-3">
<h3 class="font-semibold text-sm text-gray-800 dark:text-gray-200 break-words break-all" title="{{ $result->title ?? $result->searchname }}">
{{ $result->title ?? $result->searchname }}
</h3>
<p class="text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ $result->artist ?? '' }}">
{{ $result->artist ?? 'Unknown Artist' }}
</p>
@if(!empty($result->year))
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">{{ $result->year }}</p>
@endif
@if(!empty($result->genre))
<p class="text-xs text-blue-600 dark:text-blue-400 mt-1 truncate">{{ $result->genre }}</p>
@php
// Extract grouped release data
$releaseGuids = isset($result->grp_release_guid) ? explode(',', $result->grp_release_guid) : [];
$releaseNames = isset($result->grp_release_name) ? explode('#', $result->grp_release_name) : [];
$releaseSizes = isset($result->grp_release_size) ? explode(',', $result->grp_release_size) : [];
$releasePostDates = isset($result->grp_release_postdate) ? explode(',', $result->grp_release_postdate) : [];
$releaseAddDates = isset($result->grp_release_adddate) ? explode(',', $result->grp_release_adddate) : [];
$releaseGrabs = isset($result->grp_release_grabs) ? explode(',', $result->grp_release_grabs) : [];
$releaseNfoIds = isset($result->grp_release_nfoid) ? explode(',', $result->grp_release_nfoid) : [];
$releaseHasPreview = isset($result->grp_haspreview) ? explode(',', $result->grp_haspreview) : [];
$releaseCategories = isset($result->grp_release_catname) ? explode(',', $result->grp_release_catname) : [];
$failedCounts = isset($result->grp_release_failed) ? array_filter(explode(',', $result->grp_release_failed)) : [];
$totalFailed = array_sum($failedCounts);
// Limit to maximum 2 releases displayed
$maxReleases = 2;
$totalReleases = count($releaseGuids);
$releaseGuids = array_slice($releaseGuids, 0, $maxReleases);
$releaseNames = array_slice($releaseNames, 0, $maxReleases);
$releaseSizes = array_slice($releaseSizes, 0, $maxReleases);
$releasePostDates = array_slice($releasePostDates, 0, $maxReleases);
$releaseAddDates = array_slice($releaseAddDates, 0, $maxReleases);
$releaseGrabs = array_slice($releaseGrabs, 0, $maxReleases);
$releaseNfoIds = array_slice($releaseNfoIds, 0, $maxReleases);
$releaseHasPreview = array_slice($releaseHasPreview, 0, $maxReleases);
$releaseCategories = array_slice($releaseCategories, 0, $maxReleases);
$guid = $releaseGuids[0] ?? null;
@endphp
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow">
<div class="flex flex-row">
<!-- Album Cover -->
<div class="flex-shrink-0">
@if($guid)
<a href="{{ url('/details/' . $guid) }}" class="block">
@if(!empty($result->cover))
<img src="{{ url('/covers/music/' . $result->cover) }}"
alt="{{ $result->artist ?? '' }} - {{ $result->title ?? '' }}"
class="w-32 h-48 object-cover"
onerror="this.onerror=null;this.src='{{ url('/images/no-cover.png') }}';">
@else
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-music text-gray-400 text-2xl"></i>
</div>
@endif
</a>
@else
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-music text-gray-400 text-2xl"></i>
</div>
@endif
</div>
<!-- Album Details -->
<div class="flex-1 p-4">
<div class="flex justify-between items-start mb-2">
<div class="flex-1">
<h3 class="text-xl font-bold text-gray-900 dark:text-gray-100">{{ $result->title ?? 'Unknown Album' }}</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">{{ $result->artist ?? 'Unknown Artist' }}</p>
@if($totalFailed > 0)
<div class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-red-100 text-red-800 border border-red-200 mt-1">
<i class="fas fa-exclamation-triangle mr-1"></i>
<span>{{ $totalFailed }} failed report{{ $totalFailed > 1 ? 's' : '' }}</span>
</div>
@endif
<div class="flex items-center gap-4 mt-1 text-sm text-gray-600 dark:text-gray-400">
@if(!empty($result->year))
<span><i class="fas fa-calendar mr-1"></i> {{ $result->year }}</span>
@endif
@if(!empty($result->genre))
<span><i class="fas fa-tag mr-1"></i> {{ $result->genre }}</span>
@endif
</div>
@if(!empty($result->label))
<div class="text-xs text-gray-600 dark:text-gray-400 mt-1">
<strong>Label:</strong> {{ $result->label }}
</div>
@endif
</div>
</div>
<!-- Release Information -->
@if(!empty($releaseGuids[0]))
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
Available Releases
@if($totalReleases > $maxReleases)
<span class="text-xs font-normal text-gray-500">(Showing {{ $maxReleases }} of {{ $totalReleases }})</span>
@endif
</h4>
<div class="space-y-2">
@foreach($releaseNames as $index => $releaseName)
@if($releaseName && isset($releaseGuids[$index]))
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-2 border border-gray-200 dark:border-gray-700">
<div class="space-y-2">
<!-- Release Name -->
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="text-sm text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 font-medium block break-all" title="{{ $releaseName }}">
{{ $releaseName }}
</a>
<!-- Info Badges -->
<div class="flex flex-wrap items-center gap-1.5">
@if(isset($releaseSizes[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-hdd mr-1"></i>{{ number_format($releaseSizes[$index] / 1073741824, 2) }} GB
</span>
@endif
@if(isset($releasePostDates[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-calendar-alt mr-1"></i>{{ date('M d, Y H:i', strtotime($releasePostDates[$index])) }}
</span>
@endif
@if(isset($releaseCategories[$index]) && !empty($releaseCategories[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
<i class="fas fa-folder mr-1"></i>{{ $releaseCategories[$index] }}
</span>
@endif
@if(isset($releaseNfoIds[$index]) && !empty($releaseNfoIds[$index]))
<button type="button"
class="nfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 hover:bg-yellow-200 dark:hover:bg-yellow-800 transition cursor-pointer"
data-guid="{{ $releaseGuids[$index] }}"
title="View NFO file">
<i class="fas fa-file-alt mr-1"></i> NFO
</button>
@endif
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap items-center gap-1.5">
<a href="{{ url('/getnzb/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-600 dark:bg-green-700 text-white hover:bg-green-700 dark:hover:bg-green-800 transition">
<i class="fas fa-download mr-1"></i> Download
</a>
<button class="add-to-cart inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-600 dark:bg-blue-700 text-white hover:bg-blue-700 dark:hover:bg-blue-800 transition" data-guid="{{ $releaseGuids[$index] }}">
<i class="fas fa-shopping-cart mr-1"></i> Cart
</button>
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-600 dark:bg-gray-700 text-white hover:bg-gray-700 dark:hover:bg-gray-800 transition">
<i class="fas fa-info-circle mr-1"></i> Details
</a>
</div>
</div>
</div>
@endif
@endforeach
</div>
</div>
@endif
</div>
</a>
<div class="px-3 pb-3 flex gap-1">
<a href="{{ url('/getnzb?id=' . $result->guid) }}"
class="flex-1 px-2 py-1 bg-green-600 dark:bg-green-700 text-white text-xs rounded hover:bg-green-700 dark:hover:bg-green-800 text-center"
title="Download NZB">
<i class="fa fa-download"></i>
</a>
<a href="{{ url('/details/' . $result->guid) }}"
class="flex-1 px-2 py-1 bg-blue-600 dark:bg-blue-700 text-white text-xs rounded hover:bg-blue-700 dark:hover:bg-blue-800 text-center"
title="View Details">
<i class="fa fa-info-circle"></i>
</a>
</div>
</div>
@endforeach
@@ -169,5 +284,8 @@
@endif
</div>
</div>
<!-- NFO Modal -->
@include('partials.nfo-modal')
@endsection
+205 -96
View File
@@ -85,110 +85,200 @@
<!-- Results -->
@if(count($results) > 0)
<div class="mb-4 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-film mr-2 text-blue-600"></i>
{{ $catname ?? 'All' }} XXX
</h2>
<div class="flex items-center gap-4">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-film mr-2 text-blue-600"></i>
{{ $catname ?? 'All' }} XXX
</h2>
<x-view-toggle
current-view="covers"
covgroup="xxx"
:category="$categorytitle ?? 'All'"
parentcat="XXX"
:shows="false"
/>
</div>
<span class="text-sm text-gray-600 dark:text-gray-400">
{{ $results->total() }} results found
</span>
</div>
<!-- XXX List Table -->
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-gray-100 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider">Name</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider hidden md:table-cell">Category</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider hidden lg:table-cell">Posted</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider hidden xl:table-cell">Size</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider">Action</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
@foreach($results as $result)
@php
// Extract first values from comma-separated grouped fields
$guid = isset($result->grp_release_guid) ? explode(',', $result->grp_release_guid)[0] : null;
$searchname = isset($result->grp_release_name) ? explode('#', $result->grp_release_name)[0] : ($result->title ?? '');
$postdate = isset($result->grp_release_postdate) ? explode(',', $result->grp_release_postdate)[0] : null;
$size = isset($result->grp_release_size) ? explode(',', $result->grp_release_size)[0] : 0;
$failedCounts = isset($result->grp_release_failed) ? array_filter(explode(',', $result->grp_release_failed)) : [];
$totalFailed = array_sum($failedCounts);
@endphp
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<td class="px-4 py-3">
<div class="flex items-start">
<div class="flex-1 min-w-0">
<a href="{{ url('/details/' . $guid) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 font-medium break-words">
{{ $searchname }}
</a>
@if($totalFailed > 0)
<span class="ml-2 px-2 py-1 bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 text-xs rounded"
title="{{ $totalFailed }} user(s) reported download failure">
<i class="fa fa-exclamation-triangle mr-1"></i>Failed
</span>
@endif
<div class="text-xs text-gray-500 dark:text-gray-400 mt-1">
@if(!empty($result->genre))
<span class="mr-3">
<i class="fa fa-tag mr-1"></i>
{!! makeFieldLinks((array) $result, 'genre', 'xxx') !!}
</span>
@endif
@if(!empty($result->actors))
<span class="mr-3">
<i class="fa fa-users mr-1"></i>
{!! makeFieldLinks((array) $result, 'actors', 'xxx') !!}
</span>
@endif
@if(!empty($result->director))
<span>
<i class="fa fa-user-circle mr-1"></i>
{!! makeFieldLinks((array) $result, 'director', 'xxx') !!}
</span>
@endif
<!-- XXX Grid - Card Layout with Multiple Releases -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
@foreach($results as $result)
@php
// Extract grouped release data
$releaseGuids = isset($result->grp_release_guid) ? explode(',', $result->grp_release_guid) : [];
$releaseNames = isset($result->grp_release_name) ? explode('#', $result->grp_release_name) : [];
$releaseSizes = isset($result->grp_release_size) ? explode(',', $result->grp_release_size) : [];
$releasePostDates = isset($result->grp_release_postdate) ? explode(',', $result->grp_release_postdate) : [];
$releaseGrabs = isset($result->grp_release_grabs) ? explode(',', $result->grp_release_grabs) : [];
$releaseComments = isset($result->grp_release_comments) ? explode(',', $result->grp_release_comments) : [];
$releaseNfoIds = isset($result->grp_release_nfoid) ? explode(',', $result->grp_release_nfoid) : [];
$releaseHasPreview = isset($result->grp_haspreview) ? explode(',', $result->grp_haspreview) : [];
$releaseCategories = isset($result->grp_release_catname) ? explode(',', $result->grp_release_catname) : [];
$failedCounts = isset($result->grp_release_failed) ? array_filter(explode(',', $result->grp_release_failed)) : [];
$totalFailed = array_sum($failedCounts);
// Limit to maximum 2 releases displayed
$maxReleases = 2;
$totalReleases = count($releaseGuids);
$releaseGuids = array_slice($releaseGuids, 0, $maxReleases);
$releaseNames = array_slice($releaseNames, 0, $maxReleases);
$releaseSizes = array_slice($releaseSizes, 0, $maxReleases);
$releasePostDates = array_slice($releasePostDates, 0, $maxReleases);
$releaseGrabs = array_slice($releaseGrabs, 0, $maxReleases);
$releaseComments = array_slice($releaseComments, 0, $maxReleases);
$releaseNfoIds = array_slice($releaseNfoIds, 0, $maxReleases);
$releaseHasPreview = array_slice($releaseHasPreview, 0, $maxReleases);
$releaseCategories = array_slice($releaseCategories, 0, $maxReleases);
$guid = $releaseGuids[0] ?? null;
@endphp
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow">
<div class="flex flex-row">
<!-- Cover Image -->
<div class="flex-shrink-0">
@if($guid)
<a href="{{ url('/details/' . $guid) }}" class="block">
@if(isset($result->cover) && $result->cover == 1)
<img src="{{ url('/covers/xxx/' . $result->id . '-cover.jpg') }}"
alt="{{ $result->title }}"
class="w-32 h-48 object-cover"
onerror="this.onerror=null;this.src='{{ url('/images/no-cover.png') }}';">
@else
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-film text-gray-400 text-2xl"></i>
</div>
@endif
</a>
@else
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-film text-gray-400 text-2xl"></i>
</div>
@endif
</div>
<!-- Content Details -->
<div class="flex-1 p-4">
<div class="flex justify-between items-start mb-2">
<div class="flex-1">
<h3 class="text-xl font-bold text-gray-900 dark:text-gray-100">{{ $result->title }}</h3>
@if($totalFailed > 0)
<div class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-red-100 text-red-800 border border-red-200 mt-1">
<i class="fas fa-exclamation-triangle mr-1"></i>
<span>{{ $totalFailed }} failed report{{ $totalFailed > 1 ? 's' : '' }}</span>
</div>
@endif
<div class="text-xs text-gray-600 dark:text-gray-400 mt-2 space-y-1">
@if(!empty($result->genre))
<div>
<strong>Genre:</strong> {!! makeFieldLinks((array) $result, 'genre', 'xxx') !!}
</div>
@endif
@if(!empty($result->actors))
<div>
<strong>Actors:</strong> {!! makeFieldLinks((array) $result, 'actors', 'xxx') !!}
</div>
@endif
@if(!empty($result->director))
<div>
<strong>Director:</strong> {!! makeFieldLinks((array) $result, 'director', 'xxx') !!}
</div>
@endif
</div>
@if(isset($result->plot) && $result->plot)
<p class="text-gray-700 dark:text-gray-300 text-sm mt-2 line-clamp-2">{{ $result->plot }}</p>
@endif
</div>
</div>
<!-- Release Information -->
@if(!empty($releaseGuids[0]))
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
Available Releases
@if($totalReleases > $maxReleases)
<span class="text-xs font-normal text-gray-500">(Showing {{ $maxReleases }} of {{ $totalReleases }})</span>
@endif
</h4>
<div class="space-y-2">
@foreach($releaseNames as $index => $releaseName)
@if($releaseName && isset($releaseGuids[$index]))
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-2 border border-gray-200 dark:border-gray-700">
<div class="space-y-2">
<!-- Release Name -->
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="text-sm text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 font-medium block break-all" title="{{ $releaseName }}">
{{ $releaseName }}
</a>
<!-- Info Badges -->
<div class="flex flex-wrap items-center gap-1.5">
@if(isset($releaseSizes[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-hdd mr-1"></i>{{ number_format($releaseSizes[$index] / 1073741824, 2) }} GB
</span>
@endif
@if(isset($releasePostDates[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300">
<i class="fas fa-calendar-alt mr-1"></i>{{ date('M d, Y H:i', strtotime($releasePostDates[$index])) }}
</span>
@endif
@if(isset($releaseCategories[$index]) && !empty($releaseCategories[$index]))
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
<i class="fas fa-folder mr-1"></i>{{ $releaseCategories[$index] }}
</span>
@endif
@if(isset($releaseGrabs[$index]) && $releaseGrabs[$index] > 0)
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
<i class="fas fa-download mr-1"></i>{{ $releaseGrabs[$index] }} grabs
</span>
@endif
@if(isset($releaseNfoIds[$index]) && !empty($releaseNfoIds[$index]))
<button type="button"
class="nfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 hover:bg-yellow-200 dark:hover:bg-yellow-800 transition cursor-pointer"
data-guid="{{ $releaseGuids[$index] }}"
title="View NFO file">
<i class="fas fa-file-alt mr-1"></i> NFO
</button>
@endif
@if(isset($releaseHasPreview[$index]) && $releaseHasPreview[$index] == 1)
<button type="button"
class="preview-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 hover:bg-purple-200 dark:hover:bg-purple-800 transition cursor-pointer"
data-guid="{{ $releaseGuids[$index] }}"
title="View preview image">
<i class="fas fa-image mr-1"></i> Preview
</button>
@endif
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap items-center gap-1.5">
<a href="{{ url('/getnzb/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-600 dark:bg-green-700 text-white hover:bg-green-700 dark:hover:bg-green-800 transition">
<i class="fas fa-download mr-1"></i> Download
</a>
<button class="add-to-cart inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-600 dark:bg-blue-700 text-white hover:bg-blue-700 dark:hover:bg-blue-800 transition" data-guid="{{ $releaseGuids[$index] }}">
<i class="fas fa-shopping-cart mr-1"></i> Cart
</button>
<a href="{{ url('/details/' . $releaseGuids[$index]) }}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-600 dark:bg-gray-700 text-white hover:bg-gray-700 dark:hover:bg-gray-800 transition">
<i class="fas fa-info-circle mr-1"></i> Details
</a>
</div>
</div>
</div>
@endif
@endforeach
</div>
</div>
</td>
<td class="px-4 py-3 text-sm text-gray-600 dark:text-gray-400 hidden md:table-cell">
@php
$categoryName = isset($result->grp_release_catname) ? explode(',', $result->grp_release_catname)[0] : 'XXX';
@endphp
{{ $categoryName }}
</td>
<td class="px-4 py-3 text-sm text-gray-600 dark:text-gray-400 hidden lg:table-cell whitespace-nowrap">
@if($postdate)
{{ date('M d, Y', strtotime($postdate)) }}
<div class="text-xs text-gray-500">{{ date('H:i', strtotime($postdate)) }}</div>
@endif
</td>
<td class="px-4 py-3 text-sm text-gray-600 dark:text-gray-400 hidden xl:table-cell whitespace-nowrap">
{{ number_format($size / 1073741824, 2) }} GB
</td>
<td class="px-4 py-3">
<div class="flex items-center justify-center gap-2">
<a href="{{ url('/getnzb?id=' . $guid) }}"
class="inline-flex items-center px-3 py-1.5 bg-green-600 dark:bg-green-700 text-white text-xs rounded hover:bg-green-700 dark:hover:bg-green-800"
title="Download NZB">
<i class="fa fa-download mr-1"></i>
<span class="hidden sm:inline">Download</span>
</a>
<a href="{{ url('/details/' . $guid) }}"
class="inline-flex items-center px-3 py-1.5 bg-blue-600 dark:bg-blue-700 text-white text-xs rounded hover:bg-blue-700 dark:hover:bg-blue-800"
title="View Details">
<i class="fa fa-info-circle mr-1"></i>
<span class="hidden sm:inline">Details</span>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
</div>
</div>
@endforeach
</div>
<!-- Pagination -->
@@ -208,5 +298,24 @@
@endif
</div>
</div>
<!-- NFO Modal -->
@include('partials.nfo-modal')
<!-- Preview Modal -->
<div id="previewModal" class="hidden fixed inset-0 bg-black bg-opacity-75 items-center justify-center p-4 z-50">
<div class="relative max-w-4xl w-full">
<button type="button" data-close-preview-modal class="absolute top-4 right-4 text-white hover:text-gray-300 text-3xl font-bold z-10">
<i class="fas fa-times"></i>
</button>
<div class="text-center mb-2">
<h3 id="previewTitle" class="text-white text-lg font-semibold"></h3>
</div>
<img id="previewImage" src="" alt="Preview" class="max-w-full max-h-[90vh] mx-auto rounded-lg shadow-2xl">
<div class="text-center mt-4">
<p id="previewError" class="text-red-400 hidden"></p>
</div>
</div>
</div>
@endsection