mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
Add tvshows management to admin area
This commit is contained in:
@@ -5,86 +5,76 @@ namespace App\Http\Controllers\Admin;
|
||||
use App\Http\Controllers\BasePageController;
|
||||
use App\Models\Release;
|
||||
use App\Models\Video;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AdminShowsController extends BasePageController
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
* Display a listing of TV shows
|
||||
*/
|
||||
public function index(Request $request): void
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$this->setAdminPrefs();
|
||||
|
||||
$meta_title = $title = 'TV Shows List';
|
||||
|
||||
$tvshowname = ($request->has('showname') && ! empty($request->input('showname')) ? $request->input('showname') : '');
|
||||
$showname = $request->input('showname', '');
|
||||
$tvshowlist = Video::getRange($showname);
|
||||
|
||||
$this->smarty->assign(
|
||||
[
|
||||
'showname' => $tvshowname,
|
||||
'tvshowlist' => Video::getRange($tvshowname),
|
||||
]
|
||||
);
|
||||
|
||||
$content = $this->smarty->fetch('show-list.tpl');
|
||||
$this->smarty->assign(compact('title', 'meta_title', 'content'));
|
||||
$this->adminrender();
|
||||
return view('admin.shows.index', compact('tvshowlist', 'showname', 'title', 'meta_title'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* Show the form for editing a TV show
|
||||
*/
|
||||
public function edit(Request $request): void
|
||||
public function edit(Request $request): View|RedirectResponse
|
||||
{
|
||||
$this->setAdminPrefs();
|
||||
|
||||
switch ($request->input('action') ?? 'view') {
|
||||
case 'submit':
|
||||
if ($request->has('from') && ! empty($request->input('from'))) {
|
||||
header('Location:'.$request->input('from'));
|
||||
exit;
|
||||
}
|
||||
$action = $request->input('action', 'view');
|
||||
|
||||
header('Location:'.url('admin/show-list'));
|
||||
break;
|
||||
if ($action === 'submit') {
|
||||
if ($request->has('from') && ! empty($request->input('from'))) {
|
||||
return redirect($request->input('from'));
|
||||
}
|
||||
|
||||
case 'view':
|
||||
default:
|
||||
if ($request->has('id')) {
|
||||
$title = 'TV Show Edit';
|
||||
$show = Video::getByVideoID($request->input('id'));
|
||||
}
|
||||
break;
|
||||
return redirect()->route('admin.show-list')->with('success', 'TV show updated successfully');
|
||||
}
|
||||
|
||||
$this->smarty->assign('show', $show);
|
||||
$show = null;
|
||||
if ($request->has('id')) {
|
||||
$show = Video::getByVideoID($request->input('id'));
|
||||
}
|
||||
|
||||
if (! $show) {
|
||||
return redirect()->route('admin.show-list')->with('error', 'TV show not found');
|
||||
}
|
||||
|
||||
$meta_title = $title = 'Edit TV Show Data';
|
||||
$content = $this->smarty->fetch('show-edit.tpl');
|
||||
$this->smarty->assign(compact('title', 'meta_title', 'content'));
|
||||
$this->adminrender();
|
||||
|
||||
return view('admin.shows.edit', compact('show', 'title', 'meta_title'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* Remove video ID from releases
|
||||
*/
|
||||
public function destroy($id): void
|
||||
public function destroy(Request $request): View
|
||||
{
|
||||
$this->setAdminPrefs();
|
||||
|
||||
$id = $request->route('id');
|
||||
$success = false;
|
||||
$videoid = null;
|
||||
|
||||
if ($id) {
|
||||
$success = Release::removeVideoIdFromReleases($id);
|
||||
$this->smarty->assign('videoid', $id);
|
||||
$videoid = $id;
|
||||
}
|
||||
|
||||
$this->smarty->assign('success', $success);
|
||||
|
||||
$meta_title = $title = 'Remove Video and Episode IDs from Releases';
|
||||
$content = $this->smarty->fetch('show-remove.tpl');
|
||||
$this->smarty->assign(compact('title', 'meta_title', 'content'));
|
||||
$this->adminrender();
|
||||
|
||||
return view('admin.shows.remove', compact('success', 'videoid', 'title', 'meta_title'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
@extends('layouts.admin')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-edit mr-2"></i>{{ $title }}
|
||||
</h1>
|
||||
<a href="{{ route('admin.show-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
|
||||
<i class="fas fa-arrow-left mr-2"></i>Back to List
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Form -->
|
||||
<div class="p-6">
|
||||
<form method="POST" action="{{ url('admin/show-edit?id=' . $show['id']) }}">
|
||||
@csrf
|
||||
<input type="hidden" name="action" value="submit">
|
||||
<input type="hidden" name="id" value="{{ $show['id'] }}">
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Left Column -->
|
||||
<div class="space-y-6">
|
||||
<!-- Title -->
|
||||
<div>
|
||||
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Title <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
id="title"
|
||||
name="title"
|
||||
value="{{ $show['title'] ?? '' }}"
|
||||
readonly
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md bg-gray-100 dark:bg-gray-900">
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Title is read-only</p>
|
||||
</div>
|
||||
|
||||
<!-- Type -->
|
||||
<div>
|
||||
<label for="type" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Type
|
||||
</label>
|
||||
<select id="type"
|
||||
name="type"
|
||||
disabled
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md bg-gray-100 dark:bg-gray-900">
|
||||
<option value="0" {{ isset($show['type']) && $show['type'] == 0 ? 'selected' : '' }}>TV Show</option>
|
||||
<option value="1" {{ isset($show['type']) && $show['type'] == 1 ? 'selected' : '' }}>Film</option>
|
||||
<option value="2" {{ isset($show['type']) && $show['type'] == 2 ? 'selected' : '' }}>Anime</option>
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Type is read-only</p>
|
||||
</div>
|
||||
|
||||
<!-- Started Date -->
|
||||
<div>
|
||||
<label for="started" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Started Date
|
||||
</label>
|
||||
<input type="text"
|
||||
id="started"
|
||||
name="started"
|
||||
value="{{ $show['started'] ?? '' }}"
|
||||
readonly
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md bg-gray-100 dark:bg-gray-900">
|
||||
</div>
|
||||
|
||||
<!-- Country -->
|
||||
<div>
|
||||
<label for="countries_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Country Code
|
||||
</label>
|
||||
<input type="text"
|
||||
id="countries_id"
|
||||
name="countries_id"
|
||||
value="{{ $show['countries_id'] ?? '' }}"
|
||||
readonly
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md bg-gray-100 dark:bg-gray-900">
|
||||
</div>
|
||||
|
||||
<!-- External IDs -->
|
||||
<div class="bg-gray-50 dark:bg-gray-900 p-4 rounded-lg">
|
||||
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">External IDs</h3>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">TVDB ID</label>
|
||||
<input type="text"
|
||||
value="{{ $show['tvdb'] ?? '—' }}"
|
||||
readonly
|
||||
class="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 rounded bg-gray-100">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">IMDB ID</label>
|
||||
<input type="text"
|
||||
value="{{ $show['imdb'] ?? '—' }}"
|
||||
readonly
|
||||
class="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 rounded bg-gray-100">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">TMDB ID</label>
|
||||
<input type="text"
|
||||
value="{{ $show['tmdb'] ?? '—' }}"
|
||||
readonly
|
||||
class="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 rounded bg-gray-100">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Trakt ID</label>
|
||||
<input type="text"
|
||||
value="{{ $show['trakt'] ?? '—' }}"
|
||||
readonly
|
||||
class="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 rounded bg-gray-100">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">TVMaze ID</label>
|
||||
<input type="text"
|
||||
value="{{ $show['tvmaze'] ?? '—' }}"
|
||||
readonly
|
||||
class="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 rounded bg-gray-100">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">AniDB ID</label>
|
||||
<input type="text"
|
||||
value="{{ $show['anidb'] ?? '—' }}"
|
||||
readonly
|
||||
class="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 rounded bg-gray-100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div class="space-y-6">
|
||||
<!-- Cover Image -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Cover Image
|
||||
</label>
|
||||
<div class="border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-gray-50 dark:bg-gray-900">
|
||||
@if(!empty($show['image']))
|
||||
<img src="{{ $show['image'] }}"
|
||||
alt="{{ $show['title'] }}"
|
||||
class="max-w-full h-auto mx-auto rounded shadow-lg"
|
||||
style="max-height: 400px;"
|
||||
onerror="this.src='{{ asset('images/no-cover.png') }}'">
|
||||
@else
|
||||
<div class="flex flex-col items-center justify-center py-12 text-gray-400 dark:text-gray-500">
|
||||
<i class="fas fa-tv text-6xl mb-3"></i>
|
||||
<p>No cover image available</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
<div>
|
||||
<label for="summary" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Summary
|
||||
</label>
|
||||
<textarea id="summary"
|
||||
name="summary"
|
||||
rows="8"
|
||||
readonly
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md bg-gray-100 dark:bg-gray-900">{{ $show['summary'] ?? '' }}</textarea>
|
||||
</div>
|
||||
|
||||
<!-- Publisher -->
|
||||
<div>
|
||||
<label for="publisher" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Publisher
|
||||
</label>
|
||||
<input type="text"
|
||||
id="publisher"
|
||||
name="publisher"
|
||||
value="{{ $show['publisher'] ?? '' }}"
|
||||
readonly
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md bg-gray-100 dark:bg-gray-900">
|
||||
</div>
|
||||
|
||||
<!-- Show Info -->
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Show Information</h3>
|
||||
<dl class="space-y-2 text-sm">
|
||||
<div class="flex justify-between">
|
||||
<dt class="text-gray-500 dark:text-gray-400">ID:</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-200 font-mono">{{ $show['id'] }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<dt class="text-gray-500 dark:text-gray-400">Source:</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-200">
|
||||
@if(isset($show['source']))
|
||||
@if($show['source'] == 0)
|
||||
TVDB
|
||||
@elseif($show['source'] == 1)
|
||||
TMDB
|
||||
@elseif($show['source'] == 2)
|
||||
Trakt
|
||||
@elseif($show['source'] == 3)
|
||||
TVMaze
|
||||
@else
|
||||
Unknown
|
||||
@endif
|
||||
@else
|
||||
—
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="mt-6 flex items-center justify-between border-t border-gray-200 dark:border-gray-700 pt-6">
|
||||
<a href="{{ route('admin.show-list') }}" class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600">
|
||||
<i class="fas fa-arrow-left mr-2"></i>Back to List
|
||||
</a>
|
||||
<div class="text-sm text-yellow-600 dark:text-yellow-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>This is a view-only page. TV show data is automatically fetched from external sources.
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
@extends('layouts.admin')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-tv mr-2"></i>{{ $title }}
|
||||
</h1>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Total: {{ $tvshowlist->total() }} shows
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search Form -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
|
||||
<form method="GET" action="{{ url('admin/show-list') }}" class="flex items-center space-x-4">
|
||||
<div class="flex-1">
|
||||
<label for="showname" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Search TV Shows
|
||||
</label>
|
||||
<input type="text"
|
||||
id="showname"
|
||||
name="showname"
|
||||
value="{{ $showname }}"
|
||||
placeholder="Enter show name..."
|
||||
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md focus:ring-blue-500 focus:border-blue-500">
|
||||
</div>
|
||||
<div class="flex items-end space-x-2">
|
||||
<button type="submit" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
<i class="fas fa-search mr-2"></i>Search
|
||||
</button>
|
||||
@if($showname)
|
||||
<a href="{{ url('admin/show-list') }}" class="px-6 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
|
||||
<i class="fas fa-times mr-2"></i>Clear
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- TV Shows Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cover</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Started</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">IDs</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Publisher</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($tvshowlist as $show)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@php
|
||||
$coverPath = public_path('covers/tvshows/' . $show->id . '.jpg');
|
||||
$hasCover = file_exists($coverPath);
|
||||
@endphp
|
||||
@if($hasCover)
|
||||
<img src="{{ asset('covers/tvshows/' . $show->id . '.jpg') }}"
|
||||
alt="{{ $show->title }}"
|
||||
class="h-16 w-12 object-cover rounded shadow"
|
||||
loading="lazy">
|
||||
@else
|
||||
<div class="h-16 w-12 bg-gray-200 dark:bg-gray-700 rounded flex items-center justify-center">
|
||||
<i class="fas fa-tv text-gray-400 text-sm"></i>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-200">
|
||||
{{ $show->title }}
|
||||
</div>
|
||||
@if(!empty($show->summary))
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-1 max-w-md line-clamp-2">
|
||||
{{ Str::limit($show->summary, 100) }}
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@if($show->type == 0)
|
||||
<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
|
||||
<i class="fas fa-tv mr-1"></i>TV
|
||||
</span>
|
||||
@elseif($show->type == 1)
|
||||
<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
|
||||
<i class="fas fa-film mr-1"></i>Film
|
||||
</span>
|
||||
@elseif($show->type == 2)
|
||||
<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-pink-100 text-pink-800 dark:bg-pink-900 dark:text-pink-200">
|
||||
<i class="fas fa-dragon mr-1"></i>Anime
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $show->started ?? '—' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-xs">
|
||||
<div class="space-y-1">
|
||||
@if($show->tvdb)
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
<span class="font-semibold">TVDB:</span> {{ $show->tvdb }}
|
||||
</div>
|
||||
@endif
|
||||
@if($show->imdb)
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
<span class="font-semibold">IMDB:</span> {{ $show->imdb }}
|
||||
</div>
|
||||
@endif
|
||||
@if($show->tmdb)
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
<span class="font-semibold">TMDB:</span> {{ $show->tmdb }}
|
||||
</div>
|
||||
@endif
|
||||
@if($show->trakt)
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
<span class="font-semibold">Trakt:</span> {{ $show->trakt }}
|
||||
</div>
|
||||
@endif
|
||||
@if($show->tvmaze)
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
<span class="font-semibold">TVMaze:</span> {{ $show->tvmaze }}
|
||||
</div>
|
||||
@endif
|
||||
@if($show->anidb)
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
<span class="font-semibold">AniDB:</span> {{ $show->anidb }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-200">
|
||||
{{ $show->publisher ?? '—' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ url('admin/show-edit?id=' . $show->id) }}"
|
||||
class="text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300"
|
||||
title="Edit">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a href="{{ url('admin/show-remove/' . $show->id) }}"
|
||||
class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300"
|
||||
title="Remove from Releases">
|
||||
<i class="fas fa-unlink"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">
|
||||
@if($showname)
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-search text-4xl mb-3"></i>
|
||||
<p class="text-lg">No TV shows found for "{{ $showname }}"</p>
|
||||
<a href="{{ url('admin/show-list') }}" class="mt-3 text-blue-600 dark:text-blue-400 hover:underline">
|
||||
Clear search and view all
|
||||
</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-tv text-4xl mb-3"></i>
|
||||
<p class="text-lg">No TV shows available</p>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<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 items-center justify-between">
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Showing {{ $tvshowlist->firstItem() ?? 0 }} to {{ $tvshowlist->lastItem() ?? 0 }} of {{ $tvshowlist->total() }} shows
|
||||
</div>
|
||||
<div>
|
||||
{{ $tvshowlist->appends(['showname' => $showname])->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
@endsection
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
@extends('layouts.admin')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-unlink mr-2"></i>{{ $title }}
|
||||
</h1>
|
||||
<a href="{{ route('admin.show-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
|
||||
<i class="fas fa-arrow-left mr-2"></i>Back to List
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="p-6">
|
||||
@if($success)
|
||||
<div class="bg-green-50 dark:bg-green-900 border-l-4 border-green-500 dark:border-green-600 p-4 rounded mb-6">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fas fa-check-circle text-green-600 dark:text-green-400 text-xl"></i>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-green-800 dark:text-green-200">
|
||||
Successfully Removed Video IDs
|
||||
</h3>
|
||||
<div class="mt-2 text-sm text-green-700 dark:text-green-300">
|
||||
<p>Video ID <strong class="font-mono">{{ $videoid }}</strong> and its associated episode IDs have been successfully removed from all releases.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">
|
||||
What was removed?
|
||||
</h3>
|
||||
<ul class="space-y-2 text-gray-600 dark:text-gray-400">
|
||||
<li class="flex items-start">
|
||||
<i class="fas fa-check text-green-600 dark:text-green-400 mt-1 mr-2"></i>
|
||||
<span>All releases linked to video ID <strong class="font-mono text-gray-900 dark:text-gray-200">{{ $videoid }}</strong> have been unlinked</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<i class="fas fa-check text-green-600 dark:text-green-400 mt-1 mr-2"></i>
|
||||
<span>All episode IDs associated with this video have been removed from releases</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<i class="fas fa-info-circle text-blue-600 dark:text-blue-400 mt-1 mr-2"></i>
|
||||
<span>The TV show data itself remains in the database and can be re-linked if needed</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-red-50 dark:bg-red-900 border-l-4 border-red-500 dark:border-red-600 p-4 rounded mb-6">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fas fa-exclamation-circle text-red-600 dark:text-red-400 text-xl"></i>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-red-800 dark:text-red-200">
|
||||
Operation Failed
|
||||
</h3>
|
||||
<div class="mt-2 text-sm text-red-700 dark:text-red-300">
|
||||
<p>Failed to remove video IDs from releases. This could be due to:</p>
|
||||
<ul class="list-disc list-inside mt-2 space-y-1">
|
||||
<li>Invalid video ID provided</li>
|
||||
<li>No releases linked to this video ID</li>
|
||||
<li>Database error</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Info Box -->
|
||||
<div class="mt-6 bg-blue-50 dark:bg-blue-900 border-l-4 border-blue-500 dark:border-blue-600 p-4 rounded">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fas fa-info-circle text-blue-600 dark:text-blue-400"></i>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-blue-800 dark:text-blue-200">
|
||||
About This Operation
|
||||
</h3>
|
||||
<div class="mt-2 text-sm text-blue-700 dark:text-blue-300">
|
||||
<p>This operation removes the association between TV show data and releases in your database. It's useful when:</p>
|
||||
<ul class="list-disc list-inside mt-2 space-y-1">
|
||||
<li>Incorrect TV show data has been linked to releases</li>
|
||||
<li>You want to force releases to be re-processed for TV show matching</li>
|
||||
<li>You're cleaning up orphaned or incorrect associations</li>
|
||||
</ul>
|
||||
<p class="mt-2"><strong>Note:</strong> This does not delete the TV show from the database, only unlinks it from releases.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="mt-6 flex items-center justify-between border-t border-gray-200 dark:border-gray-700 pt-6">
|
||||
<a href="{{ route('admin.show-list') }}" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
<i class="fas fa-list mr-2"></i>Return to TV Shows List
|
||||
</a>
|
||||
@if($videoid)
|
||||
<a href="{{ url('admin/show-edit?id=' . $videoid) }}" class="px-6 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
|
||||
<i class="fas fa-eye mr-2"></i>View TV Show Details
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -79,6 +79,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TV Shows -->
|
||||
<div class="mb-4">
|
||||
<button type="button" class="flex items-center justify-between w-full text-left text-gray-300 dark:text-gray-400 hover:text-white dark:hover:text-white transition" onclick="toggleAdminSubmenu('shows-menu')">
|
||||
<div class="flex items-center space-x-3">
|
||||
<i class="fas fa-tv"></i>
|
||||
<span>TV Shows</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down text-sm transform transition-transform" id="shows-menu-icon"></i>
|
||||
</button>
|
||||
<div id="shows-menu" class="hidden mt-2 ml-6 space-y-1">
|
||||
<a href="{{ url('/admin/show-list') }}" class="block py-2 px-3 text-gray-400 dark:text-gray-500 hover:text-white dark:hover:text-white hover:bg-gray-800 dark:hover:bg-gray-800 rounded transition">TV Shows List</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Games -->
|
||||
<div class="mb-4">
|
||||
<button type="button" class="flex items-center justify-between w-full text-left text-gray-300 dark:text-gray-400 hover:text-white dark:hover:text-white transition" onclick="toggleAdminSubmenu('games-menu')">
|
||||
|
||||
+39
-39
@@ -165,69 +165,69 @@ Route::middleware('isVerified')->group(function () {
|
||||
});
|
||||
|
||||
Route::middleware('role:Admin', '2fa')->prefix('admin')->group(function () {
|
||||
Route::match(['GET', 'POST'], 'index', [AdminPageController::class, 'index'])->name('admin.index');
|
||||
Route::match(['GET', 'POST'], 'anidb-delete/{id}', [AdminAnidbController::class, 'destroy'])->name('admin.anidb-delete');
|
||||
Route::get('index', [AdminPageController::class, 'index'])->name('admin.index');
|
||||
Route::post('anidb-delete/{id}', [AdminAnidbController::class, 'destroy'])->name('admin.anidb-delete');
|
||||
Route::match(['GET', 'POST'], 'anidb-edit/{id}', [AdminAnidbController::class, 'edit'])->name('admin.anidb-edit');
|
||||
Route::match(['GET', 'POST'], 'anidb-list', [AdminAnidbController::class, 'index'])->name('admin.anidb-list');
|
||||
Route::match(['GET', 'POST'], 'binaryblacklist-list', [AdminBlacklistController::class, 'index'])->name('admin.binaryblacklist-list');
|
||||
Route::get('anidb-list', [AdminAnidbController::class, 'index'])->name('admin.anidb-list');
|
||||
Route::get('binaryblacklist-list', [AdminBlacklistController::class, 'index'])->name('admin.binaryblacklist-list');
|
||||
Route::match(['GET', 'POST'], 'binaryblacklist-edit', [AdminBlacklistController::class, 'edit'])->name('admin.binaryblacklist-edit');
|
||||
Route::match(['GET', 'POST'], 'book-list', [AdminBookController::class, 'index'])->name('admin.book-list');
|
||||
Route::get('book-list', [AdminBookController::class, 'index'])->name('admin.book-list');
|
||||
Route::match(['GET', 'POST'], 'book-edit', [AdminBookController::class, 'edit'])->name('admin.book-edit');
|
||||
Route::match(['GET', 'POST'], 'category-list', [AdminCategoryController::class, 'index'])->name('admin.category-list');
|
||||
Route::get('category-list', [AdminCategoryController::class, 'index'])->name('admin.category-list');
|
||||
Route::match(['GET', 'POST'], 'category-edit', [AdminCategoryController::class, 'edit'])->name('admin.category-edit');
|
||||
Route::match(['GET', 'POST'], 'user-list', [AdminUserController::class, 'index'])->name('admin.user-list');
|
||||
Route::get('user-list', [AdminUserController::class, 'index'])->name('admin.user-list');
|
||||
Route::match(['GET', 'POST'], 'user-edit', [AdminUserController::class, 'edit'])->name('admin.user-edit');
|
||||
Route::match(['GET', 'POST'], 'user-delete', [AdminUserController::class, 'destroy'])->name('admin.user-delete');
|
||||
Route::match(['GET', 'POST'], 'verify', [AdminUserController::class, 'verify'])->name('admin.verify');
|
||||
Route::match(['GET', 'POST'], 'resendverification', [AdminUserController::class, 'resendVerification'])->name('admin.resend-verification');
|
||||
Route::post('user-delete', [AdminUserController::class, 'destroy'])->name('admin.user-delete');
|
||||
Route::post('verify', [AdminUserController::class, 'verify'])->name('admin.verify');
|
||||
Route::post('resendverification', [AdminUserController::class, 'resendVerification'])->name('admin.resend-verification');
|
||||
Route::match(['GET', 'POST'], 'site-edit', [AdminSiteController::class, 'edit'])->name('admin.site-edit');
|
||||
Route::match(['GET', 'POST'], 'site-stats', [AdminSiteController::class, 'stats'])->name('admin.site-stats');
|
||||
Route::match(['GET', 'POST'], 'role-list', [AdminRoleController::class, 'index'])->name('admin.role-list');
|
||||
Route::get('site-stats', [AdminSiteController::class, 'stats'])->name('admin.site-stats');
|
||||
Route::get('role-list', [AdminRoleController::class, 'index'])->name('admin.role-list');
|
||||
Route::match(['GET', 'POST'], 'role-add', [AdminRoleController::class, 'create'])->name('admin.role-add');
|
||||
Route::match(['GET', 'POST'], 'role-edit', [AdminRoleController::class, 'edit'])->name('admin.role-edit');
|
||||
Route::match(['GET', 'POST'], 'role-delete', [AdminRoleController::class, 'destroy'])->name('admin.role-delete');
|
||||
Route::match(['GET', 'POST'], 'content-list', [AdminContentController::class, 'index'])->name('admin.content-list');
|
||||
Route::post('role-delete', [AdminRoleController::class, 'destroy'])->name('admin.role-delete');
|
||||
Route::get('content-list', [AdminContentController::class, 'index'])->name('admin.content-list');
|
||||
Route::match(['GET', 'POST'], 'content-add', [AdminContentController::class, 'create'])->name('admin.content-add');
|
||||
Route::match(['GET', 'POST'], 'content-delete', [AdminContentController::class, 'destroy'])->name('admin.content-delete');
|
||||
Route::match(['GET', 'POST'], 'category_regexes-list', [AdminCategoryRegexesController::class, 'index'])->name('admin.category-regexes-list');
|
||||
Route::post('content-delete', [AdminContentController::class, 'destroy'])->name('admin.content-delete');
|
||||
Route::get('category_regexes-list', [AdminCategoryRegexesController::class, 'index'])->name('admin.category-regexes-list');
|
||||
Route::match(['GET', 'POST'], 'category_regexes-edit', [AdminCategoryRegexesController::class, 'edit'])->name('admin.category-regexes-edit');
|
||||
Route::match(['GET', 'POST'], 'collection_regexes-list', [AdminCollectionRegexesController::class, 'index'])->name('admin.collection-regexes-list');
|
||||
Route::get('collection_regexes-list', [AdminCollectionRegexesController::class, 'index'])->name('admin.collection-regexes-list');
|
||||
Route::match(['GET', 'POST'], 'collection_regexes-edit', [AdminCollectionRegexesController::class, 'edit'])->name('admin.collection-regexes-edit');
|
||||
Route::match(['GET', 'POST'], 'collection_regexes-test', [AdminCollectionRegexesController::class, 'testRegex'])->name('admin.collection-regexes-test');
|
||||
Route::match(['GET', 'POST'], 'release_naming_regexes-list', [AdminReleaseNamingRegexesController::class, 'index'])->name('admin.release-naming-regexes-list');
|
||||
Route::post('collection_regexes-test', [AdminCollectionRegexesController::class, 'testRegex'])->name('admin.collection-regexes-test');
|
||||
Route::get('release_naming_regexes-list', [AdminReleaseNamingRegexesController::class, 'index'])->name('admin.release-naming-regexes-list');
|
||||
Route::match(['GET', 'POST'], 'release_naming_regexes-edit', [AdminReleaseNamingRegexesController::class, 'edit'])->name('admin.release-naming-regexes-edit');
|
||||
Route::match(['GET', 'POST'], 'release_naming_regexes-test', [AdminReleaseNamingRegexesController::class, 'testRegex'])->name('admin.release-naming-regexes-test');
|
||||
Route::match(['GET', 'POST'], 'ajax', [AdminAjaxController::class, 'ajaxAction'])->name('admin.ajax');
|
||||
Route::post('release_naming_regexes-test', [AdminReleaseNamingRegexesController::class, 'testRegex'])->name('admin.release-naming-regexes-test');
|
||||
Route::post('ajax', [AdminAjaxController::class, 'ajaxAction'])->name('admin.ajax');
|
||||
Route::match(['GET', 'POST'], 'tmux-edit', [AdminTmuxController::class, 'edit'])->name('admin.tmux-edit');
|
||||
Route::match(['GET', 'POST'], 'release-list', [AdminReleasesController::class, 'index'])->name('admin.release-list');
|
||||
Route::match(['GET', 'POST'], 'release-delete/{id}', [AdminReleasesController::class, 'destroy'])->name('admin.release-delete');
|
||||
Route::match(['GET', 'POST'], 'show-list', [AdminShowsController::class, 'index'])->name('admin.show-list');
|
||||
Route::get('release-list', [AdminReleasesController::class, 'index'])->name('admin.release-list');
|
||||
Route::post('release-delete/{id}', [AdminReleasesController::class, 'destroy'])->name('admin.release-delete');
|
||||
Route::get('show-list', [AdminShowsController::class, 'index'])->name('admin.show-list');
|
||||
Route::match(['GET', 'POST'], 'show-edit', [AdminShowsController::class, 'edit'])->name('admin.show-edit');
|
||||
Route::match(['GET', 'POST'], 'show-remove', [AdminShowsController::class, 'destroy'])->name('admin.show-remove');
|
||||
Route::match(['GET', 'POST'], 'comments-list', [AdminCommentsController::class, 'index'])->name('admin.comments-list');
|
||||
Route::match(['GET', 'POST'], 'comments-delete/{id}', [AdminCommentsController::class, 'destroy'])->name('admin.comments-delete');
|
||||
Route::match(['GET', 'POST'], 'console-list', [AdminConsoleController::class, 'index'])->name('admin.console-list');
|
||||
Route::get('show-remove/{id}', [AdminShowsController::class, 'destroy'])->name('admin.show-remove');
|
||||
Route::get('comments-list', [AdminCommentsController::class, 'index'])->name('admin.comments-list');
|
||||
Route::post('comments-delete/{id}', [AdminCommentsController::class, 'destroy'])->name('admin.comments-delete');
|
||||
Route::get('console-list', [AdminConsoleController::class, 'index'])->name('admin.console-list');
|
||||
Route::match(['GET', 'POST'], 'console-edit', [AdminConsoleController::class, 'edit'])->name('admin.console-edit');
|
||||
Route::match(['GET', 'POST'], 'failrel-list', [AdminFailedReleasesController::class, 'index'])->name('admin.failrel-list');
|
||||
Route::match(['GET', 'POST'], 'game-list', [AdminGameController::class, 'index'])->name('admin.game-list');
|
||||
Route::get('failrel-list', [AdminFailedReleasesController::class, 'index'])->name('admin.failrel-list');
|
||||
Route::get('game-list', [AdminGameController::class, 'index'])->name('admin.game-list');
|
||||
Route::match(['GET', 'POST'], 'game-edit', [AdminGameController::class, 'edit'])->name('admin.game-edit');
|
||||
Route::match(['GET', 'POST'], 'movie-list', [AdminMovieController::class, 'index'])->name('admin.movie-list');
|
||||
Route::get('movie-list', [AdminMovieController::class, 'index'])->name('admin.movie-list');
|
||||
Route::match(['GET', 'POST'], 'movie-edit', [AdminMovieController::class, 'edit'])->name('admin.movie-edit');
|
||||
Route::match(['GET', 'POST'], 'movie-add', [AdminMovieController::class, 'create'])->name('admin.movie-add');
|
||||
Route::match(['GET', 'POST'], 'music-list', [AdminMusicController::class, 'index'])->name('admin.music-list');
|
||||
Route::get('music-list', [AdminMusicController::class, 'index'])->name('admin.music-list');
|
||||
Route::match(['GET', 'POST'], 'music-edit', [AdminMusicController::class, 'edit'])->name('admin.music-edit');
|
||||
Route::match(['GET', 'POST'], 'predb', [AdminPredbController::class, 'index'])->name('admin.predb');
|
||||
Route::match(['GET', 'POST'], 'group-list', [AdminGroupController::class, 'index'])->name('admin.group-list');
|
||||
Route::get('predb', [AdminPredbController::class, 'index'])->name('admin.predb');
|
||||
Route::get('group-list', [AdminGroupController::class, 'index'])->name('admin.group-list');
|
||||
Route::match(['GET', 'POST'], 'group-edit', [AdminGroupController::class, 'edit'])->name('admin.group-edit');
|
||||
Route::match(['GET', 'POST'], 'group-bulk', [AdminGroupController::class, 'createBulk'])->name('admin.group-bulk');
|
||||
Route::match(['GET', 'POST'], 'group-list-active', [AdminGroupController::class, 'active'])->name('admin.group-list-active');
|
||||
Route::match(['GET', 'POST'], 'group-list-inactive', [AdminGroupController::class, 'inactive'])->name('admin.group-list-inactive');
|
||||
Route::get('group-list-active', [AdminGroupController::class, 'active'])->name('admin.group-list-active');
|
||||
Route::get('group-list-inactive', [AdminGroupController::class, 'inactive'])->name('admin.group-list-inactive');
|
||||
|
||||
// Deleted Users Management Routes
|
||||
Route::match(['GET', 'POST'], 'deleted-users', [DeletedUsersController::class, 'index'])->name('admin.deleted.users.index');
|
||||
Route::get('deleted-users', [DeletedUsersController::class, 'index'])->name('admin.deleted.users.index');
|
||||
Route::post('deleted-users/bulk', [DeletedUsersController::class, 'bulkAction'])->name('admin.deleted.users.bulk');
|
||||
Route::match(['GET', 'POST'], 'deleted-users/restore/{id}', [DeletedUsersController::class, 'restore'])->name('admin.deleted.users.restore');
|
||||
Route::match(['GET', 'POST'], 'deleted-users/permanent-delete/{id}', [DeletedUsersController::class, 'permanentDelete'])->name('admin.deleted.users.permanent-delete');
|
||||
Route::post('deleted-users/restore/{id}', [DeletedUsersController::class, 'restore'])->name('admin.deleted.users.restore');
|
||||
Route::post('deleted-users/permanent-delete/{id}', [DeletedUsersController::class, 'permanentDelete'])->name('admin.deleted.users.permanent-delete');
|
||||
});
|
||||
|
||||
Route::middleware('role_or_permission:Admin|Moderator|edit release')->prefix('admin')->group(function () {
|
||||
|
||||
Reference in New Issue
Block a user