feat(admin): rebuild release upload form with Livewire and CDN validation
Replace the server-rendered release upload form with a Livewire component, introducing an interactive admin release form that supports dynamic episode management, real‑time CDN path validation, and direct download file checks. Key changes: - New `AdminReleaseForm` Livewire component with dynamic episode fields, Tagify integration, and upload handling via Livewire's file uploads. - Added `CdnPathValidator` service to verify download and stream paths across all configured mirrors, caching results and capturing file sizes. - Extended `EpisodeService` and `GalleryService` to support array‑based data and temporary uploaded files from Livewire. - Persist validated download sizes and store the validation timestamp on the `downloads` table via a new migration; also add a unique constraint on `hentais.slug` to prevent duplicates. - Remove the old POST `/admin/release/upload` route and controller logic, along with the legacy `upload.js` script. - Add comprehensive feature and unit tests covering the new component and the CDN validation service.
This commit is contained in:
@@ -4,14 +4,16 @@ namespace App\Services;
|
||||
|
||||
use App\Models\Episode;
|
||||
use App\Models\Hentai;
|
||||
use App\Models\Studios;
|
||||
use App\Models\ModLog;
|
||||
use App\Models\Studios;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\Encoders\WebpEncoder;
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
|
||||
class EpisodeService
|
||||
{
|
||||
@@ -171,19 +173,60 @@ class EpisodeService
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an episode from an array of data (used by the Livewire release form).
|
||||
*
|
||||
* @param array{title: string, title_jpn: string, baseurl: string, description: string, releasedate: string, tags: string[], interpolated_uhd?: bool} $data
|
||||
*/
|
||||
public function createEpisodeFromArray(
|
||||
array $data,
|
||||
Hentai $hentai,
|
||||
int $episodeNumber,
|
||||
?Studios $studio = null
|
||||
): Episode {
|
||||
$episode = new Episode;
|
||||
$episode->title = $data['title'];
|
||||
$episode->title_search = preg_replace('/[^A-Za-z0-9 ]/', '', $episode->title);
|
||||
$episode->title_jpn = $data['title_jpn'];
|
||||
$episode->slug = "{$hentai->slug}-{$episodeNumber}";
|
||||
$episode->hentai_id = $hentai->id;
|
||||
$episode->studios_id = $studio->id;
|
||||
$episode->episode = $episodeNumber;
|
||||
$episode->description = $data['description'];
|
||||
$episode->url = rtrim($data['baseurl'], '/').'/E'.str_pad($episodeNumber, 2, '0', STR_PAD_LEFT);
|
||||
$episode->view_count = 0;
|
||||
$episode->interpolated = true;
|
||||
$episode->interpolated_uhd = $data['interpolated_uhd'] ?? false;
|
||||
$episode->is_dvd_aspect = false;
|
||||
$episode->release_date = Carbon::parse($data['releasedate'])->format('Y-m-d');
|
||||
$episode->cover_url = "/images/hentai/{$hentai->slug}/cover-ep-{$episodeNumber}.webp";
|
||||
$episode->save();
|
||||
|
||||
foreach ($data['tags'] as $tag) {
|
||||
$episode->tag($tag);
|
||||
}
|
||||
|
||||
return $episode;
|
||||
}
|
||||
|
||||
public function createOrUpdateCover(Request $request, Episode $episode, string $slug, int $episodeNumber): void
|
||||
{
|
||||
if (! $request->hasFile("episodecover{$episodeNumber}")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->saveCoverFromFile($episode, $slug, $episodeNumber, $request->file("episodecover{$episodeNumber}"));
|
||||
}
|
||||
|
||||
public function saveCoverFromFile(Episode $episode, string $slug, int $episodeNumber, UploadedFile|TemporaryUploadedFile $file): void
|
||||
{
|
||||
// Create Folder for Image Upload
|
||||
if (! Storage::disk('public')->exists("/images/hentai/{$slug}")) {
|
||||
Storage::disk('public')->makeDirectory("/images/hentai/{$slug}");
|
||||
}
|
||||
|
||||
// Encode and save cover image
|
||||
Image::read($request->file("episodecover{$episodeNumber}")->getRealPath())
|
||||
Image::read($file->getRealPath())
|
||||
->cover(268, 394)
|
||||
->encode(new WebpEncoder)
|
||||
->save(Storage::disk('public')->path($episode->cover_url));
|
||||
|
||||
Reference in New Issue
Block a user