Compare commits
15 Commits
laravel-12
...
2480c5b309
| Author | SHA1 | Date | |
|---|---|---|---|
| 2480c5b309 | |||
| 4fc11d7329 | |||
| f3e5100d5d | |||
| 5b4d3d435e | |||
| 564f816fb9 | |||
| 3709e378c3 | |||
| 4a45dae593 | |||
| 2b0448d517 | |||
| 3bb6af73c3 | |||
| 57cf153560 | |||
| e45fd4b148 | |||
|
d479369770
|
|||
| b8ba17b33f | |||
| 5a8dd12cb8 | |||
| 3a77c4320d |
@@ -2,13 +2,13 @@
|
||||
|
||||
## hstream Website
|
||||
|
||||
### Install
|
||||
### Install (Ubuntu)
|
||||
|
||||
```bash
|
||||
# Install PHP
|
||||
sudo add-apt-repository ppa:ondrej/php
|
||||
apt update && apt upgrade
|
||||
apt install php8.3 php8.3-xml php8.3-mysql php8.3-gd php8.3-zip php8.3-curl php8.3-mbstring
|
||||
apt install php8.4 php8.4-xml php8.4-mysql php8.4-gd php8.4-zip php8.4-curl php8.4-mbstring
|
||||
|
||||
# Install NodeJS
|
||||
curl -sL https://deb.nodesource.com/setup_20.x -o /tmp/nodesource_setup.sh
|
||||
@@ -22,7 +22,7 @@ mv composer.phar composer
|
||||
|
||||
# Install NGINX (skip for local dev)
|
||||
apt install nginx
|
||||
apt install php8.3-fpm
|
||||
apt install php8.4-fpm
|
||||
|
||||
# Install MariaDB
|
||||
apt install mariadb-server
|
||||
@@ -54,7 +54,7 @@ nano /etc/supervisor/conf.d/laravel-queue.conf :
|
||||
|
||||
[program:laravel-queue]
|
||||
process_name=%(program_name)s_%(process_num)02d
|
||||
command=php /var/www/hstream/artisan queue:work --queue=default --sleep=3 --tries=3 --max-time=3600
|
||||
command=php84 /var/www/hstream/artisan queue:work --queue=default --sleep=3 --tries=3 --max-time=3600
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopasgroup=true
|
||||
@@ -83,9 +83,9 @@ zip -r hstream_2023_11_30.zip hstream/
|
||||
|
||||
### Update
|
||||
```bash
|
||||
php artisan down
|
||||
php84 artisan down
|
||||
git pull
|
||||
npm run build
|
||||
php artisan view:clear && php artisan optimize:clear && php artisan cache:clear && service php8.4-fpm restart
|
||||
php artisan up
|
||||
php84 artisan view:clear && php84 artisan optimize:clear && php84 artisan cache:clear && service php8.4-fpm restart
|
||||
php84 artisan up
|
||||
```
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\PopularDaily;
|
||||
use App\Models\PopularWeekly;
|
||||
use App\Models\PopularMonthly;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Models\PopularWeekly;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class AutoStats extends Command
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Episode;
|
||||
use App\Models\Hentai;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Spatie\Sitemap\Sitemap;
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Downloads;
|
||||
use App\Jobs\GetFileSizeFromCDN;
|
||||
|
||||
use App\Models\Downloads;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class GetFileSize extends Command
|
||||
|
||||
@@ -4,9 +4,8 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\UserDownload;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class ResetUserDownloads extends Command
|
||||
{
|
||||
|
||||
+17
-16
@@ -5,21 +5,20 @@ namespace App\Helpers;
|
||||
use App\Models\Comment;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Hentai;
|
||||
use App\Models\PopularDaily;
|
||||
use App\Models\PopularMonthly;
|
||||
use App\Models\PopularWeekly;
|
||||
use App\Models\PopularDaily;
|
||||
|
||||
use Conner\Tagging\Model\Tag;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CacheHelper
|
||||
{
|
||||
public static function getRecentlyReleased(bool $guest)
|
||||
{
|
||||
$guestString = $guest ? 'guest' : 'authed';
|
||||
return Cache::remember("recently_released_".$guestString, now()->addMinutes(60), function () use ($guest) {
|
||||
|
||||
return Cache::remember('recently_released_'.$guestString, now()->addMinutes(60), function () use ($guest) {
|
||||
return Episode::with('gallery')
|
||||
->when($guest, fn ($query) => $query->withoutTags(['loli', 'shota']))
|
||||
->orderBy('release_date', 'desc')
|
||||
@@ -31,7 +30,8 @@ class CacheHelper
|
||||
public static function getRecentlyUploaded(bool $guest)
|
||||
{
|
||||
$guestString = $guest ? 'guest' : 'authed';
|
||||
return Cache::remember("recently_uploaded".$guestString, now()->addMinutes(5), function () use ($guest) {
|
||||
|
||||
return Cache::remember('recently_uploaded'.$guestString, now()->addMinutes(5), function () use ($guest) {
|
||||
return Episode::with('gallery')
|
||||
->when($guest, fn ($query) => $query->withoutTags(['loli', 'shota']))
|
||||
->orderBy('created_at', 'desc')
|
||||
@@ -42,21 +42,21 @@ class CacheHelper
|
||||
|
||||
public static function getTotalViewCount()
|
||||
{
|
||||
return Cache::remember("total_view_count", now()->addMinutes(60), function () {
|
||||
return Cache::remember('total_view_count', now()->addMinutes(60), function () {
|
||||
return Episode::sum('view_count');
|
||||
});
|
||||
}
|
||||
|
||||
public static function getTotalEpisodeCount()
|
||||
{
|
||||
return Cache::remember("total_episode_count", now()->addMinutes(60), function () {
|
||||
return Cache::remember('total_episode_count', now()->addMinutes(60), function () {
|
||||
return Episode::count();
|
||||
});
|
||||
}
|
||||
|
||||
public static function getTotalHentaiCount()
|
||||
{
|
||||
return Cache::remember("total_hentai_count", now()->addMinutes(60), function () {
|
||||
return Cache::remember('total_hentai_count', now()->addMinutes(60), function () {
|
||||
return Hentai::count();
|
||||
});
|
||||
}
|
||||
@@ -64,7 +64,8 @@ class CacheHelper
|
||||
public static function getPopularAllTime(bool $guest)
|
||||
{
|
||||
$guestString = $guest ? 'guest' : 'authed';
|
||||
return Cache::remember("top_hentai_alltime".$guestString, now()->addMinutes(360), function () use ($guest) {
|
||||
|
||||
return Cache::remember('top_hentai_alltime'.$guestString, now()->addMinutes(360), function () use ($guest) {
|
||||
return Episode::with('gallery')
|
||||
->when($guest, fn ($query) => $query->withoutTags(['loli', 'shota']))
|
||||
->orderBy('view_count', 'desc')
|
||||
@@ -75,7 +76,7 @@ class CacheHelper
|
||||
|
||||
public static function getPopularMonthly()
|
||||
{
|
||||
return Cache::remember("top_hentai_monthly", now()->addMinutes(360), function () {
|
||||
return Cache::remember('top_hentai_monthly', now()->addMinutes(360), function () {
|
||||
return PopularMonthly::groupBy('episode_id')
|
||||
->select('episode_id', DB::raw('count(*) as total'))
|
||||
->with('episode.gallery')
|
||||
@@ -87,7 +88,7 @@ class CacheHelper
|
||||
|
||||
public static function getPopularWeekly()
|
||||
{
|
||||
return Cache::remember("top_hentai_weekly", now()->addMinutes(360), function () {
|
||||
return Cache::remember('top_hentai_weekly', now()->addMinutes(360), function () {
|
||||
return PopularWeekly::groupBy('episode_id')
|
||||
->select('episode_id', DB::raw('count(*) as total'))
|
||||
->with('episode.gallery')
|
||||
@@ -100,7 +101,7 @@ class CacheHelper
|
||||
|
||||
public static function getPopularDaily()
|
||||
{
|
||||
return Cache::remember("top_hentai_daily", now()->addMinutes(30), function () {
|
||||
return Cache::remember('top_hentai_daily', now()->addMinutes(30), function () {
|
||||
return PopularDaily::groupBy('episode_id')
|
||||
->select('episode_id', DB::raw('count(*) as total'))
|
||||
->with('episode.gallery')
|
||||
@@ -112,21 +113,21 @@ class CacheHelper
|
||||
|
||||
public static function getMostLikes()
|
||||
{
|
||||
return Cache::remember("top_likes", now()->addMinutes(30), function () {
|
||||
return Cache::remember('top_likes', now()->addMinutes(30), function () {
|
||||
return DB::table('markable_likes')->groupBy('markable_id')->select('markable_id', DB::raw('count(*) as total'))->orderBy('total', 'desc')->limit(16)->get();
|
||||
});
|
||||
}
|
||||
|
||||
public static function getAllTags()
|
||||
{
|
||||
return Cache::remember("all_tags", now()->addMinutes(10080), function () {
|
||||
return Cache::remember('all_tags', now()->addMinutes(10080), function () {
|
||||
return Tag::where('count', '>', 0)->orderBy('slug', 'ASC')->get();
|
||||
});
|
||||
}
|
||||
|
||||
public static function getLatestComments()
|
||||
{
|
||||
return Cache::remember("latest_comments", now()->addMinutes(60), function () {
|
||||
return Cache::remember('latest_comments', now()->addMinutes(60), function () {
|
||||
return Comment::latest()->take(10)->get();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class GitHelper
|
||||
{
|
||||
public static function shortCommit()
|
||||
{
|
||||
return Cache::remember("git_commit", now()->addMinutes(60), function () {
|
||||
return Cache::remember('git_commit', now()->addMinutes(60), function () {
|
||||
try {
|
||||
return trim(exec('git rev-parse --short HEAD'));
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -15,7 +15,7 @@ class ContactController extends Controller
|
||||
$contacts = Contact::orderBy('created_at', 'DESC')->get();
|
||||
|
||||
return view('admin.contact.index', [
|
||||
'contacts' => $contacts
|
||||
'contacts' => $contacts,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Episode;
|
||||
|
||||
use App\Jobs\DiscordReleaseNotification;
|
||||
use App\Models\Episode;
|
||||
use App\Services\DownloadService;
|
||||
use App\Services\EpisodeService;
|
||||
use App\Services\GalleryService;
|
||||
@@ -14,7 +13,9 @@ use Illuminate\Http\Request;
|
||||
class EpisodeController extends Controller
|
||||
{
|
||||
protected EpisodeService $episodeService;
|
||||
|
||||
protected GalleryService $galleryService;
|
||||
|
||||
protected DownloadService $downloadService;
|
||||
|
||||
public function __construct(
|
||||
@@ -43,7 +44,7 @@ class EpisodeController extends Controller
|
||||
|
||||
// Discord Alert
|
||||
if ($request->has('censored')) {
|
||||
DiscordReleaseNotification::dispatch($referenceEpisode->title." - ".$episodeNumber, 'release-censored');
|
||||
DiscordReleaseNotification::dispatch($referenceEpisode->title.' - '.$episodeNumber, 'release-censored');
|
||||
} else {
|
||||
DiscordReleaseNotification::dispatch($episode->slug, 'release');
|
||||
}
|
||||
@@ -51,7 +52,7 @@ class EpisodeController extends Controller
|
||||
cache()->flush();
|
||||
|
||||
return to_route('hentai.index', [
|
||||
'title' => $episode->slug
|
||||
'title' => $episode->slug,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@ class EpisodeController extends Controller
|
||||
cache()->flush();
|
||||
|
||||
return to_route('hentai.index', [
|
||||
'title' => $episode->slug
|
||||
'title' => $episode->slug,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Hentai;
|
||||
|
||||
use App\Jobs\DiscordReleaseNotification;
|
||||
use App\Models\Hentai;
|
||||
use App\Services\DownloadService;
|
||||
use App\Services\EpisodeService;
|
||||
use App\Services\GalleryService;
|
||||
@@ -14,7 +13,9 @@ use Illuminate\Http\Request;
|
||||
class ReleaseController extends Controller
|
||||
{
|
||||
protected EpisodeService $episodeService;
|
||||
|
||||
protected GalleryService $galleryService;
|
||||
|
||||
protected DownloadService $downloadService;
|
||||
|
||||
public function __construct(
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use App\Models\SiteBackground;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
use Intervention\Image\Encoders\WebpEncoder;
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
|
||||
class SiteBackgroundController extends Controller
|
||||
{
|
||||
@@ -44,7 +42,7 @@ class SiteBackgroundController extends Controller
|
||||
$bg = SiteBackground::create(array_merge(
|
||||
$request->only(['date_start', 'date_end']),
|
||||
[
|
||||
'default' => (bool) $request->input('default', false)
|
||||
'default' => (bool) $request->input('default', false),
|
||||
]
|
||||
));
|
||||
|
||||
@@ -55,13 +53,14 @@ class SiteBackgroundController extends Controller
|
||||
|
||||
Image::read($file->getRealPath())
|
||||
->scaleDown(height: $resolution)
|
||||
->encode(new WebpEncoder())
|
||||
->encode(new WebpEncoder)
|
||||
->save(public_path($targetPath));
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error($e->getMessage());
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ class SiteBackgroundController extends Controller
|
||||
SiteBackground::where('id', $request->input('id'))->update(array_merge(
|
||||
$request->only(['date_start', 'date_end']),
|
||||
[
|
||||
'default' => (bool) $request->input('default', false)
|
||||
'default' => (bool) $request->input('default', false),
|
||||
]
|
||||
));
|
||||
|
||||
@@ -116,6 +115,7 @@ class SiteBackgroundController extends Controller
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error($e->getMessage());
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Episode;
|
||||
use App\Models\EpisodeSubtitle;
|
||||
use App\Models\Subtitle;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SubtitleController extends Controller
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use App\Models\User;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UserController extends Controller
|
||||
@@ -27,7 +27,6 @@ class UserController extends Controller
|
||||
'action' => 'required',
|
||||
]);
|
||||
|
||||
|
||||
$user = User::findOrFail($validated['id']);
|
||||
|
||||
switch ($validated['action']) {
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\CacheHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Studios;
|
||||
use App\Models\Subtitle;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AdminApiController extends Controller
|
||||
{
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Downloads;
|
||||
use App\Models\Episode;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DownloadApiController extends Controller
|
||||
{
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Hentai;
|
||||
use App\Models\PopularMonthly;
|
||||
use Carbon\Carbon;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class HentaiApiController extends Controller
|
||||
{
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use App\Models\Episode;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class StreamApiController extends Controller
|
||||
@@ -33,7 +31,7 @@ class StreamApiController extends Controller
|
||||
'stream_url' => $episode->dmca_takedown ? 'stuff/dmca' : $episode->url,
|
||||
'stream_domains' => config('hstream.stream_domain'),
|
||||
'asia_stream_domains' => config('hstream.asia_stream_domain'),
|
||||
'extra_subtitles' => $subtitles
|
||||
'extra_subtitles' => $subtitles,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\CacheHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Conner\Tagging\Model\Tag;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UserApiController extends Controller
|
||||
{
|
||||
@@ -33,11 +32,10 @@ class UserApiController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return response()->json([
|
||||
'message' => 'success',
|
||||
'tags' => $tagWhiteList,
|
||||
'usertags' => $tagBlackList
|
||||
'usertags' => $tagBlackList,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use App\Models\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -90,6 +88,7 @@ class DiscordAuthController extends Controller
|
||||
// User is not in the guild
|
||||
if ($response->status() === 404) {
|
||||
$user->removeRole(UserRole::SUPPORTER);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,6 +113,7 @@ class DiscordAuthController extends Controller
|
||||
if (! $hasSupporterRole) {
|
||||
// Remove role if not found
|
||||
$user->removeRole(UserRole::SUPPORTER);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ class PasswordController extends Controller
|
||||
public function update(Request $request): RedirectResponse
|
||||
{
|
||||
// If user logged in with Discord and has not yet a password, allow to set password
|
||||
if ($request->user()->discord_id && is_null($request->user()->password))
|
||||
{
|
||||
if ($request->user()->discord_id && is_null($request->user()->password)) {
|
||||
$validated = $request->validateWithBag('updatePassword', [
|
||||
'password' => ['required', Password::defaults(), 'confirmed'],
|
||||
]);
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -11,8 +12,6 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules;
|
||||
|
||||
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||
|
||||
class RegisteredUserController extends Controller
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Contact;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ContactController extends Controller
|
||||
{
|
||||
@@ -30,7 +29,7 @@ class ContactController extends Controller
|
||||
'altcha' => ['required', new ValidAltcha],
|
||||
]);
|
||||
|
||||
$contact = new Contact();
|
||||
$contact = new Contact;
|
||||
$contact->name = $request->input('name');
|
||||
$contact->email = $request->input('email');
|
||||
$contact->message = $request->input('message');
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Episode;
|
||||
use App\Helpers\CacheHelper;
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cookie;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
@@ -19,15 +18,14 @@ class HomeController extends Controller
|
||||
|
||||
$guestString = $guest ? 'guest' : 'authed';
|
||||
|
||||
$mostLikes = \cache()->remember('mostLikes'.$guestString, 300, fn () =>
|
||||
Episode::with('gallery')
|
||||
$mostLikes = \cache()->remember('mostLikes'.$guestString, 300, fn () => Episode::with('gallery')
|
||||
->when($guest, fn ($query) => $query->withoutTags(['loli', 'shota']))
|
||||
->whereIn('id', function ($query) {
|
||||
$mostLikesIds = CacheHelper::getMostLikes()->pluck('markable_id')->toArray();
|
||||
$query->selectRaw('id')
|
||||
->from('episodes')
|
||||
->whereIn('id', $mostLikesIds)
|
||||
->orderByRaw("FIELD(id, " . implode(',', $mostLikesIds) . ")");
|
||||
->orderByRaw('FIELD(id, '.implode(',', $mostLikesIds).')');
|
||||
})
|
||||
->get()
|
||||
);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\MatrixRegisterRequest;
|
||||
use App\Services\MatrixRegistrationService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MatrixController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the user page.
|
||||
*/
|
||||
public function index(Request $request): \Illuminate\View\View
|
||||
{
|
||||
$rooms = [
|
||||
['name' => '🏠 General', 'description' => 'Our main chat.', 'alias' => 'https://matrix.to/#/#general:hstream.moe'],
|
||||
['name' => '📡 Releases', 'description' => 'Were we @everyone for new releases.', 'alias' => 'https://matrix.to/#/#releases:hstream.moe'],
|
||||
['name' => '👗 NSFW 2D', 'description' => 'Channel for R18 2D Media.', 'alias' => 'https://matrix.to/#/#nsfw:hstream.moe'],
|
||||
['name' => '👗 NSFW IRL', 'description' => 'Channel for R18 IRL Media.', 'alias' => 'https://matrix.to/#/#nsfw-irl:hstream.moe'],
|
||||
];
|
||||
|
||||
return view('matrix.index', [
|
||||
'user' => $request->user(),
|
||||
'rooms' => $rooms,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create matrix user
|
||||
*/
|
||||
public function store(
|
||||
MatrixRegisterRequest $request,
|
||||
MatrixRegistrationService $matrixService
|
||||
) {
|
||||
try {
|
||||
$result = $matrixService->registerUser(
|
||||
$request->username,
|
||||
$request->password
|
||||
);
|
||||
|
||||
$user = $request->user();
|
||||
$user->matrix_id = $result['user_id'];
|
||||
$user->save();
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', 'Matrix user created successfully.');
|
||||
} catch (\Exception $e) {
|
||||
return back()
|
||||
->withErrors([
|
||||
'username' => $e->getMessage(),
|
||||
])
|
||||
->withInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class NotificationController extends Controller
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Models\Playlist;
|
||||
use App\Models\PlaylistEpisode;
|
||||
use App\Services\PlaylistService;
|
||||
use Illuminate\Http\Request;
|
||||
use RealRashid\SweetAlert\Facades\Alert;
|
||||
|
||||
class PlaylistController extends Controller
|
||||
{
|
||||
@@ -42,14 +41,13 @@ class PlaylistController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the user's playlists page.
|
||||
*/
|
||||
public function playlists(Request $request): \Illuminate\View\View
|
||||
{
|
||||
$title = 'Delete Playlist!';
|
||||
$text = "Are you sure you want to delete?";
|
||||
$text = 'Are you sure you want to delete?';
|
||||
confirmDelete($title, $text);
|
||||
|
||||
return view('profile.playlists', [
|
||||
@@ -85,7 +83,7 @@ class PlaylistController extends Controller
|
||||
'name' => 'required|max:30',
|
||||
]);
|
||||
|
||||
$playlist = new Playlist();
|
||||
$playlist = new Playlist;
|
||||
$playlist->user_id = $request->user()->id;
|
||||
$playlist->name = $request->input('name');
|
||||
$playlist->is_private = $request->input('visiblity') === 'private';
|
||||
@@ -151,7 +149,7 @@ class PlaylistController extends Controller
|
||||
|
||||
$validated = $request->validate([
|
||||
'playlist' => 'required|max:30',
|
||||
'episode_id' => 'required'
|
||||
'episode_id' => 'required',
|
||||
]);
|
||||
|
||||
$playlist = Playlist::where('user_id', $user->id)->where('id', $request->input('playlist'))->firstOrFail();
|
||||
@@ -161,7 +159,7 @@ class PlaylistController extends Controller
|
||||
$exists = PlaylistEpisode::where('playlist_id', $playlist->id)->where('episode_id', $episode->id)->exists();
|
||||
if ($exists) {
|
||||
return response()->json([
|
||||
'message' => 'already-added'
|
||||
'message' => 'already-added',
|
||||
], 200);
|
||||
}
|
||||
|
||||
@@ -175,7 +173,7 @@ class PlaylistController extends Controller
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'success'
|
||||
'message' => 'success',
|
||||
], 200);
|
||||
}
|
||||
|
||||
@@ -188,7 +186,7 @@ class PlaylistController extends Controller
|
||||
'name' => 'required|max:30',
|
||||
]);
|
||||
|
||||
$playlist = new Playlist();
|
||||
$playlist = new Playlist;
|
||||
$playlist->user_id = $request->user()->id;
|
||||
$playlist->name = $request->input('name');
|
||||
$playlist->is_private = $request->input('visiblity') === 'private';
|
||||
@@ -196,7 +194,7 @@ class PlaylistController extends Controller
|
||||
|
||||
return response()->json([
|
||||
'message' => 'success',
|
||||
'playlist_id' => $playlist->id
|
||||
'playlist_id' => $playlist->id,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use App\Models\Episode;
|
||||
use App\Models\User;
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use Conner\Tagging\Model\Tag;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
|
||||
use Conner\Tagging\Model\Tag;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
@@ -121,6 +119,7 @@ class ProfileController extends Controller
|
||||
if (! $tags) {
|
||||
$user->tag_blacklist = null;
|
||||
$user->save();
|
||||
|
||||
return Redirect::route('profile.settings')->with('status', 'blacklist-updated');
|
||||
}
|
||||
|
||||
@@ -177,8 +176,8 @@ class ProfileController extends Controller
|
||||
protected function storeAvatar(\Illuminate\Http\UploadedFile $file, User $user): void
|
||||
{
|
||||
// Create Folder for Image Upload
|
||||
if (! Storage::disk('public')->exists("/images/avatars")) {
|
||||
Storage::disk('public')->makeDirectory("/images/avatars");
|
||||
if (! Storage::disk('public')->exists('/images/avatars')) {
|
||||
Storage::disk('public')->makeDirectory('/images/avatars');
|
||||
}
|
||||
|
||||
// Delete old avatar if it exists
|
||||
@@ -196,5 +195,4 @@ class ProfileController extends Controller
|
||||
|
||||
$user->avatar = $filename;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\CacheHelper;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Gallery;
|
||||
use App\Models\Hentai;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistEpisode;
|
||||
use App\Models\Watched;
|
||||
use App\Helpers\CacheHelper;
|
||||
|
||||
use hisorange\BrowserDetect\Facade as Browser;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use hisorange\BrowserDetect\Facade as Browser;
|
||||
|
||||
class StreamController extends Controller
|
||||
{
|
||||
/**
|
||||
@@ -37,7 +35,6 @@ class StreamController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$episode = Episode::where('slug', $title)->firstOrFail();
|
||||
$gallery = Gallery::where('episode_id', $episode->id)->get();
|
||||
$moreEpisodes = Episode::with(['gallery', 'studio'])->where('hentai_id', $episode->hentai_id)->whereNot('id', $episode->id)->get();
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
<?php namespace app\Http\Middleware;
|
||||
<?php
|
||||
|
||||
namespace app\Http\Middleware;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class IsAdmin {
|
||||
|
||||
class IsAdmin
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if(Auth::check() && Auth::user()->hasRole(UserRole::ADMINISTRATOR))
|
||||
{
|
||||
if (Auth::check() && Auth::user()->hasRole(UserRole::ADMINISTRATOR)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
session()->flash('error_msg', 'This resource is restricted to Administrators!');
|
||||
|
||||
return redirect()->route('home.index');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
<?php namespace app\Http\Middleware;
|
||||
<?php
|
||||
|
||||
namespace app\Http\Middleware;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class IsBanned {
|
||||
|
||||
class IsBanned
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if(Auth::check() && Auth::user()->hasRole(UserRole::BANNED))
|
||||
{
|
||||
if (Auth::check() && Auth::user()->hasRole(UserRole::BANNED)) {
|
||||
Auth::logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect()->route('home.banned');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -18,12 +17,12 @@ class IsModerator
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (Auth::check() && Auth::user()->hasRole(UserRole::MODERATOR))
|
||||
{
|
||||
if (Auth::check() && Auth::user()->hasRole(UserRole::MODERATOR)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
session()->flash('error_msg', 'This resource is restricted to Administrators!');
|
||||
|
||||
return redirect()->route('home.index');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,14 @@ class SetLocale
|
||||
// 1. Logged-in user preference
|
||||
if (Auth::check() && Auth::user()->locale) {
|
||||
App::setLocale(Auth::user()->locale);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// 2. Session (guest or user override)
|
||||
if (session()->has('locale') && in_array($request->language, config('app.supported_locales'), true)) {
|
||||
App::setLocale(session('locale'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -9,8 +10,6 @@ use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MatrixRegisterRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
$isOldEnough = $this->user()->created_at->lt(now()->subMonth());
|
||||
$noAccount = ! $this->user()->matrix_id;
|
||||
|
||||
return $isOldEnough && $noAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'username' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:3',
|
||||
'max:32',
|
||||
'regex:/^[a-z0-9._=-]+$/', // Valid Matrix localpart
|
||||
],
|
||||
'password' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:8',
|
||||
'confirmed',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'username.regex' => 'Username may only contain lowercase letters, numbers and . _ = -',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class ProfileUpdateRequest extends FormRequest
|
||||
'nullable',
|
||||
'image',
|
||||
'mimes:jpg,png,jpeg,webp,gif',
|
||||
'max:8192'
|
||||
'max:8192',
|
||||
],
|
||||
'email' => [
|
||||
'required',
|
||||
|
||||
@@ -7,7 +7,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use Spatie\DiscordAlerts\Facades\DiscordAlert;
|
||||
|
||||
class DiscordReleaseNotification implements ShouldQueue
|
||||
@@ -32,26 +31,25 @@ class DiscordReleaseNotification implements ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
switch($this->messageType)
|
||||
{
|
||||
switch ($this->messageType) {
|
||||
case 'release':
|
||||
DiscordAlert::message("<@&868457842250764289> (´• ω •`)ノ New **4k** Release! Check it out here: https://hstream.moe/hentai/".$this->slug);
|
||||
DiscordAlert::message('<@&868457842250764289> (´• ω •`)ノ New **4k** Release! Check it out here: https://hstream.moe/hentai/'.$this->slug);
|
||||
break;
|
||||
case 'release-censored':
|
||||
# Because Discord TOS
|
||||
DiscordAlert::message("<@&868457842250764289> (´• ω •`)ノ New **4k** Release: ".$this->slug." - *No link here because of* :pLoli:");
|
||||
// Because Discord TOS
|
||||
DiscordAlert::message('<@&868457842250764289> (´• ω •`)ノ New **4k** Release: '.$this->slug.' - *No link here because of* :pLoli:');
|
||||
break;
|
||||
case 'update':
|
||||
# 1080p 48fps added
|
||||
DiscordAlert::to('update')->message("<@&1283518462584426598> (´• ω •`)ノ Added **48fps** to Release! Check it out here: https://hstream.moe/hentai/".$this->slug);
|
||||
// 1080p 48fps added
|
||||
DiscordAlert::to('update')->message('<@&1283518462584426598> (´• ω •`)ノ Added **48fps** to Release! Check it out here: https://hstream.moe/hentai/'.$this->slug);
|
||||
break;
|
||||
case 'updateUHD':
|
||||
# 4k 48fps added
|
||||
DiscordAlert::to('update')->message("<@&1326860920902778963> (´• ω •`)ノ Added **48fps 4k** to Release! Check it out here: https://hstream.moe/hentai/".$this->slug);
|
||||
// 4k 48fps added
|
||||
DiscordAlert::to('update')->message('<@&1326860920902778963> (´• ω •`)ノ Added **48fps 4k** to Release! Check it out here: https://hstream.moe/hentai/'.$this->slug);
|
||||
break;
|
||||
case 'v2':
|
||||
# v2 re-release
|
||||
DiscordAlert::to('rerelease')->message("<@&1425505303075754035> (´• ω •`)ノ **v2 Re-**Release! Check it out here: https://hstream.moe/hentai/".$this->slug);
|
||||
// v2 re-release
|
||||
DiscordAlert::to('rerelease')->message('<@&1425505303075754035> (´• ω •`)ノ **v2 Re-**Release! Check it out here: https://hstream.moe/hentai/'.$this->slug);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -7,12 +7,12 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class GetFileSizeFromCDN implements ShouldQueue
|
||||
{
|
||||
@@ -37,6 +37,7 @@ class GetFileSizeFromCDN implements ShouldQueue
|
||||
$download = Downloads::find($this->downloadId);
|
||||
if (! $download) {
|
||||
Log::error("Download not found for ID: {$this->downloadId}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace App\Livewire;
|
||||
use App\Models\Comment;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AdminCommentSearch extends Component
|
||||
{
|
||||
@@ -41,7 +40,7 @@ class AdminCommentSearch extends Component
|
||||
->paginate(12);
|
||||
|
||||
return view('livewire.admin-comment-search', [
|
||||
'comments' => $comments
|
||||
'comments' => $comments,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,9 @@ namespace App\Livewire;
|
||||
use App\Enums\UserRole;
|
||||
use App\Models\Comment;
|
||||
use App\Models\User;
|
||||
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Livewire\Attributes\Url;
|
||||
|
||||
class AdminUserSearch extends Component
|
||||
{
|
||||
@@ -46,7 +45,7 @@ class AdminUserSearch extends Component
|
||||
->paginate(20);
|
||||
|
||||
return view('livewire.admin-user-search', [
|
||||
'users' => $users
|
||||
'users' => $users,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Livewire\Attributes\Url;
|
||||
|
||||
use App\Models\SiteBackground;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
class BackgroundImages extends Component
|
||||
{
|
||||
@@ -20,16 +19,14 @@ class BackgroundImages extends Component
|
||||
{
|
||||
$now = Carbon::now();
|
||||
|
||||
$images = SiteBackground::when($this->filter === 'active', fn ($query) =>
|
||||
$query->whereDate('date_start', '<=', $now)->whereDate('date_end', '>=', $now)
|
||||
$images = SiteBackground::when($this->filter === 'active', fn ($query) => $query->whereDate('date_start', '<=', $now)->whereDate('date_end', '>=', $now)
|
||||
)
|
||||
->when($this->filter === 'inactive', fn ($query) =>
|
||||
$query->whereDate('date_start', '>', $now)->orWhereDate('date_end', '<', $now)
|
||||
->when($this->filter === 'inactive', fn ($query) => $query->whereDate('date_start', '>', $now)->orWhereDate('date_end', '<', $now)
|
||||
)
|
||||
->paginate(10);
|
||||
|
||||
return view('livewire.background-images', [
|
||||
'images' => $images
|
||||
'images' => $images,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-16
@@ -1,20 +1,16 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Episode;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CommentNotification;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
use Maize\Markable\Models\Like;
|
||||
|
||||
class Comment extends Component
|
||||
@@ -30,21 +26,21 @@ class Comment extends Component
|
||||
public $liked = false;
|
||||
|
||||
public $replyState = [
|
||||
'body' => ''
|
||||
'body' => '',
|
||||
];
|
||||
|
||||
public $isEditing = false;
|
||||
|
||||
public $editState = [
|
||||
'body' => ''
|
||||
'body' => '',
|
||||
];
|
||||
|
||||
protected $listeners = [
|
||||
'refresh' => '$refresh'
|
||||
'refresh' => '$refresh',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'replyState.body' => 'reply'
|
||||
'replyState.body' => 'reply',
|
||||
];
|
||||
|
||||
public function updatedIsEditing($isEditing)
|
||||
@@ -54,7 +50,7 @@ class Comment extends Component
|
||||
}
|
||||
|
||||
$this->editState = [
|
||||
'body' => $this->comment->body
|
||||
'body' => $this->comment->body,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -79,7 +75,8 @@ class Comment extends Component
|
||||
public function postReply()
|
||||
{
|
||||
if (! ($this->comment->depth() < 2)) {
|
||||
$this->addError('replyState.body', "Too many sub comments.");
|
||||
$this->addError('replyState.body', 'Too many sub comments.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -91,13 +88,14 @@ class Comment extends Component
|
||||
$seconds = RateLimiter::availableIn($rateLimitKey);
|
||||
|
||||
$this->addError('replyState.body', "Too many comments. Try again in {$seconds} seconds.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RateLimiter::hit($rateLimitKey, $rateLimitMinutes);
|
||||
|
||||
$this->validate([
|
||||
'replyState.body' => 'required'
|
||||
'replyState.body' => 'required',
|
||||
]);
|
||||
|
||||
$reply = $this->comment->children()->make($this->replyState);
|
||||
@@ -123,7 +121,7 @@ class Comment extends Component
|
||||
}
|
||||
|
||||
$this->replyState = [
|
||||
'body' => ''
|
||||
'body' => '',
|
||||
];
|
||||
|
||||
$this->isReplying = false;
|
||||
@@ -144,6 +142,7 @@ class Comment extends Component
|
||||
if ($this->liked) {
|
||||
$this->liked = false;
|
||||
$this->likeCount--;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
|
||||
class Comments extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
@@ -14,21 +13,21 @@ class Comments extends Component
|
||||
public $model;
|
||||
|
||||
public $newCommentState = [
|
||||
'body' => ''
|
||||
'body' => '',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'newCommentState.body' => 'comment'
|
||||
'newCommentState.body' => 'comment',
|
||||
];
|
||||
|
||||
protected $listeners = [
|
||||
'refresh' => '$refresh'
|
||||
'refresh' => '$refresh',
|
||||
];
|
||||
|
||||
public function postComment()
|
||||
{
|
||||
$this->validate([
|
||||
'newCommentState.body' => 'required'
|
||||
'newCommentState.body' => 'required',
|
||||
]);
|
||||
|
||||
$user = auth()->user();
|
||||
@@ -39,6 +38,7 @@ class Comments extends Component
|
||||
$seconds = RateLimiter::availableIn($rateLimitKey);
|
||||
|
||||
$this->addError('newCommentState.body', "Too many comments. Try again in {$seconds} seconds.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class Comments extends Component
|
||||
$comment->save();
|
||||
|
||||
$this->newCommentState = [
|
||||
'body' => ''
|
||||
'body' => '',
|
||||
];
|
||||
|
||||
$this->resetPage();
|
||||
@@ -65,7 +65,7 @@ class Comments extends Component
|
||||
->paginate(50);
|
||||
|
||||
return view('livewire.comments', [
|
||||
'comments' => $comments
|
||||
'comments' => $comments,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,15 @@ class DownloadButton extends Component
|
||||
|
||||
public $background = 'bg-rose-600';
|
||||
|
||||
public $fileExtension = 'HEVC';
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (str_contains($this->downloadUrl, 'AV1')) {
|
||||
$this->fileExtension = 'AV1';
|
||||
}
|
||||
}
|
||||
|
||||
public function clicked($downloadId)
|
||||
{
|
||||
$download = Downloads::find($downloadId);
|
||||
|
||||
@@ -5,11 +5,9 @@ namespace App\Livewire;
|
||||
use App\Models\Episode;
|
||||
use App\Models\User;
|
||||
use App\Models\UserDownload;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
|
||||
class DownloadsFree extends Component
|
||||
{
|
||||
@@ -51,6 +49,7 @@ class DownloadsFree extends Component
|
||||
if (Carbon::parse($alreadyDownloaded->created_at)->addHours(6) <= Carbon::now()) {
|
||||
// Already expired
|
||||
$alreadyDownloaded->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,6 +64,7 @@ class DownloadsFree extends Component
|
||||
if ($user->downloads_left <= 0) {
|
||||
// Daily limit reached
|
||||
$this->granted = 3;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Downloads;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Livewire\Attributes\Url;
|
||||
|
||||
class DownloadsSearch extends Component
|
||||
{
|
||||
@@ -25,6 +25,7 @@ class DownloadsSearch extends Component
|
||||
|
||||
#[Url(history: true)]
|
||||
public $studios = [];
|
||||
|
||||
public $studiosCopy = [];
|
||||
|
||||
// To toggle individual option selection
|
||||
@@ -144,7 +145,9 @@ class DownloadsSearch extends Component
|
||||
|
||||
$downloads = Downloads::when($this->fileSearch != '', fn ($query) => $query->where('url', 'like', '%'.$this->fileSearch.'%'))
|
||||
->whereIn('type', $this->getSelectedTypes())
|
||||
->when($this->studios !== [], fn ($q) => $q->whereHas('episode', fn ($query) => $query->whereHas('studio', function ($query) { $query->whereIn('slug', $this->studios); })))
|
||||
->when($this->studios !== [], fn ($q) => $q->whereHas('episode', fn ($query) => $query->whereHas('studio', function ($query) {
|
||||
$query->whereIn('slug', $this->studios);
|
||||
})))
|
||||
->whereNotNull('size')
|
||||
->orderBy($orderby, $orderdirection)
|
||||
->paginate(20);
|
||||
|
||||
@@ -4,10 +4,9 @@ namespace App\Livewire;
|
||||
|
||||
use App\Models\Episode;
|
||||
use App\Models\User;
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
use Livewire\Component;
|
||||
use Maize\Markable\Models\Like;
|
||||
|
||||
class LikeButton extends Component
|
||||
@@ -52,6 +51,7 @@ class LikeButton extends Component
|
||||
if ($this->liked) {
|
||||
$this->liked = false;
|
||||
$this->likeCount--;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Livewire\Attributes\Url;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class LiveSearch extends Component
|
||||
{
|
||||
@@ -20,14 +20,17 @@ class LiveSearch extends Component
|
||||
|
||||
#[Url(history: true)]
|
||||
public $tags = [];
|
||||
|
||||
public $tagsCopy = [];
|
||||
|
||||
#[Url(history: true)]
|
||||
public $studios = [];
|
||||
|
||||
public $studiosCopy = [];
|
||||
|
||||
#[Url(history: true)]
|
||||
public $blacklist = [];
|
||||
|
||||
public $blacklistCopy = [];
|
||||
|
||||
#[Url(history: true)]
|
||||
@@ -118,10 +121,14 @@ class LiveSearch extends Component
|
||||
}
|
||||
|
||||
$user_id = Auth::check() ? auth()->user()->id : 0;
|
||||
$episodes = Episode::with('gallery')->when($this->search != '', fn ($query) => $query->where(function($query) { $query->where('title', 'like', '%'.$this->search.'%')->orWhere('title_search', 'like', '%'.$this->search.'%')->orWhere('title_jpn', 'like', '%'.$this->search.'%'); }))
|
||||
$episodes = Episode::with('gallery')->when($this->search != '', fn ($query) => $query->where(function ($query) {
|
||||
$query->where('title', 'like', '%'.$this->search.'%')->orWhere('title_search', 'like', '%'.$this->search.'%')->orWhere('title_jpn', 'like', '%'.$this->search.'%');
|
||||
}))
|
||||
->when($this->tags !== [], fn ($query) => $query->withAllTags($this->tags))
|
||||
->when($this->blacklist !== [], fn ($query) => $query->withoutTags($this->blacklist))
|
||||
->when($this->studios !== [], fn ($query) => $query->whereHas('studio', function ($query) { $query->whereIn('slug', $this->studios); }))
|
||||
->when($this->studios !== [], fn ($query) => $query->whereHas('studio', function ($query) {
|
||||
$query->whereIn('slug', $this->studios);
|
||||
}))
|
||||
->when($this->hideWatched !== [] && Auth::check(), fn ($query) => $query->whereDoesntHave('watched', function ($query) use ($user_id) {
|
||||
$query->where('user_id', $user_id);
|
||||
}))
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Episode;
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
|
||||
class NavLiveSearch extends Component
|
||||
{
|
||||
|
||||
@@ -5,13 +5,11 @@ namespace App\Livewire;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistEpisode;
|
||||
use App\Services\PlaylistService;
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Livewire\Attributes\Url;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class PlaylistOverview extends Component
|
||||
{
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Playlist;
|
||||
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Livewire\Attributes\Url;
|
||||
|
||||
class Playlists extends Component
|
||||
{
|
||||
@@ -59,7 +58,7 @@ class Playlists extends Component
|
||||
->paginate($this->pagination);
|
||||
|
||||
return view('livewire.playlists', [
|
||||
'playlists' => $playlists
|
||||
'playlists' => $playlists,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Comment;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
@@ -36,14 +35,13 @@ class UserComments extends Component
|
||||
$orderdirection = 'desc';
|
||||
}
|
||||
|
||||
|
||||
$comments = Comment::where('user_id', $this->model->id)
|
||||
->when($this->commentSearch != '', fn ($query) => $query->where('body', 'like', '%'.$this->commentSearch.'%'))
|
||||
->orderBy($orderby, $orderdirection)
|
||||
->paginate(10);
|
||||
->paginate(6);
|
||||
|
||||
return view('livewire.user-comments', [
|
||||
'comments' => $comments
|
||||
'comments' => $comments,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use Livewire\Attributes\Url;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UserLikes extends Component
|
||||
{
|
||||
@@ -20,14 +20,17 @@ class UserLikes extends Component
|
||||
|
||||
#[Url(history: true)]
|
||||
public $tags = [];
|
||||
|
||||
public $tagsCopy = [];
|
||||
|
||||
#[Url(history: true)]
|
||||
public $studios = [];
|
||||
|
||||
public $studiosCopy = [];
|
||||
|
||||
#[Url(history: true)]
|
||||
public $blacklist = [];
|
||||
|
||||
public $blacklistCopy = [];
|
||||
|
||||
#[Url(history: true)]
|
||||
@@ -119,10 +122,14 @@ class UserLikes extends Component
|
||||
|
||||
$user_id = Auth::check() ? auth()->user()->id : 0;
|
||||
$episodes = Episode::whereHasLike(auth()->user())
|
||||
->when($this->search !== '', fn ($query) => $query->where(function($query) { $query->where('title', 'like', '%'.$this->search.'%')->orWhere('title_search', 'like', '%'.$this->search.'%')->orWhere('title_jpn', 'like', '%'.$this->search.'%'); }))
|
||||
->when($this->search !== '', fn ($query) => $query->where(function ($query) {
|
||||
$query->where('title', 'like', '%'.$this->search.'%')->orWhere('title_search', 'like', '%'.$this->search.'%')->orWhere('title_jpn', 'like', '%'.$this->search.'%');
|
||||
}))
|
||||
->when($this->tags !== [], fn ($query) => $query->withAllTags($this->tags))
|
||||
->when($this->blacklist !== [], fn ($query) => $query->withoutTags($this->blacklist))
|
||||
->when($this->studios !== [], fn ($query) => $query->whereHas('studio', function ($query) { $query->whereIn('slug', $this->studios); }))
|
||||
->when($this->studios !== [], fn ($query) => $query->whereHas('studio', function ($query) {
|
||||
$query->whereIn('slug', $this->studios);
|
||||
}))
|
||||
->when($this->hideWatched !== [] && Auth::check(), fn ($query) => $query->whereDoesntHave('watched', function ($query) use ($user_id) {
|
||||
$query->where('user_id', $user_id);
|
||||
}))
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Watched as UserWatched;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
@@ -4,19 +4,18 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Presenters\CommentPresenter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Maize\Markable\Markable;
|
||||
use Maize\Markable\Models\Like;
|
||||
|
||||
class Comment extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes, Markable;
|
||||
use HasFactory, Markable, SoftDeletes;
|
||||
|
||||
protected static $marks = [
|
||||
Like::class
|
||||
Like::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -25,7 +24,7 @@ class Comment extends Model
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'body'
|
||||
'body',
|
||||
];
|
||||
|
||||
public function presenter()
|
||||
|
||||
+13
-18
@@ -2,35 +2,28 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Downloads;
|
||||
use App\Models\PopularMonthly;
|
||||
use App\Models\PopularWeekly;
|
||||
use App\Models\PopularDaily;
|
||||
|
||||
use Conner\Tagging\Taggable;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Maize\Markable\Markable;
|
||||
use Maize\Markable\Models\Like;
|
||||
|
||||
use Spatie\Sitemap\Contracts\Sitemapable;
|
||||
use Spatie\Sitemap\Tags\Url;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Maize\Markable\Markable;
|
||||
use Maize\Markable\Models\Like;
|
||||
use Spatie\Sitemap\Contracts\Sitemapable;
|
||||
use Spatie\Sitemap\Tags\Url;
|
||||
|
||||
class Episode extends Model implements Sitemapable
|
||||
{
|
||||
use Markable, Taggable;
|
||||
use HasFactory;
|
||||
use Markable, Taggable;
|
||||
use Searchable;
|
||||
|
||||
protected static $marks = [
|
||||
Like::class
|
||||
Like::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -180,6 +173,7 @@ class Episode extends Model implements Sitemapable
|
||||
|
||||
$problematicResults .= $pTag;
|
||||
}
|
||||
|
||||
return $problematicResults;
|
||||
}
|
||||
|
||||
@@ -234,9 +228,10 @@ class Episode extends Model implements Sitemapable
|
||||
return $this->hasMany(Watched::class);
|
||||
}
|
||||
|
||||
public function getDownloadByType(string $type): Downloads | null
|
||||
public function getDownloadByType(string $type): ?Downloads
|
||||
{
|
||||
$cacheKey = "episode_{$this->id}_download_{$type}";
|
||||
|
||||
return Cache::remember($cacheKey, now()->addMinutes(10), function () use ($type) {
|
||||
return $this->downloads()->where('type', $type)->first();
|
||||
});
|
||||
|
||||
@@ -2,19 +2,18 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Conner\Tagging\Taggable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Spatie\Sitemap\Contracts\Sitemapable;
|
||||
use Spatie\Sitemap\Tags\Url;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Conner\Tagging\Taggable;
|
||||
|
||||
class Hentai extends Model implements Sitemapable
|
||||
{
|
||||
use Taggable;
|
||||
use HasFactory;
|
||||
use Taggable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -31,7 +30,7 @@ class Hentai extends Model implements Sitemapable
|
||||
return $this->hasMany(Episode::class, 'hentai_id');
|
||||
}
|
||||
|
||||
public function title(): String
|
||||
public function title(): string
|
||||
{
|
||||
return $this->episodes->first()->title;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Playlist extends Model
|
||||
{
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PlaylistEpisode extends Model
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class SiteBackground extends Model
|
||||
{
|
||||
@@ -16,7 +15,7 @@ class SiteBackground extends Model
|
||||
protected $fillable = [
|
||||
'date_start',
|
||||
'date_end',
|
||||
'default'
|
||||
'default',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Studios extends Model
|
||||
{
|
||||
|
||||
+4
-8
@@ -5,13 +5,12 @@ namespace App\Models;
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
@@ -62,7 +61,6 @@ class User extends Authenticatable
|
||||
'discord_avatar' => 'string',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Has Many Playlists.
|
||||
*/
|
||||
@@ -108,13 +106,11 @@ class User extends Authenticatable
|
||||
*/
|
||||
public function getAvatar(): string
|
||||
{
|
||||
if ($this->discord_id && $this->discord_avatar && !$this->avatar)
|
||||
{
|
||||
if ($this->discord_id && $this->discord_avatar && ! $this->avatar) {
|
||||
return "https://external-content.duckduckgo.com/iu/?u={$this->discord_avatar}";
|
||||
}
|
||||
|
||||
if ($this->avatar)
|
||||
{
|
||||
if ($this->avatar) {
|
||||
return Storage::url($this->avatar);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class UserDownload extends Model
|
||||
{
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Watched extends Model
|
||||
{
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class CommentNotification extends Notification
|
||||
@@ -12,7 +10,9 @@ class CommentNotification extends Notification
|
||||
use Queueable;
|
||||
|
||||
protected $type;
|
||||
|
||||
protected $message;
|
||||
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Comment;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class CommentPolicy
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Jobs\GetFileSizeFromCDN;
|
||||
use App\Models\Downloads;
|
||||
use App\Models\Episode;
|
||||
|
||||
use App\Jobs\GetFileSizeFromCDN;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DownloadService
|
||||
|
||||
@@ -5,14 +5,12 @@ namespace App\Services;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Hentai;
|
||||
use App\Models\Studios;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\Encoders\WebpEncoder;
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
|
||||
class EpisodeService
|
||||
{
|
||||
@@ -24,6 +22,7 @@ class EpisodeService
|
||||
|
||||
if (is_numeric($lastPart) && $lastPart < 1000) {
|
||||
$slugParts[array_key_last($slugParts)] = 's'.$lastPart;
|
||||
|
||||
return implode('-', $slugParts);
|
||||
}
|
||||
|
||||
@@ -36,11 +35,10 @@ class EpisodeService
|
||||
int $episodeNumber,
|
||||
?Studios $studio = null,
|
||||
?Episode $referenceEpisode = null
|
||||
): Episode
|
||||
{
|
||||
$episode = new Episode();
|
||||
): Episode {
|
||||
$episode = new Episode;
|
||||
$episode->title = $referenceEpisode->title ?? $request->input('title');
|
||||
$episode->title_search = preg_replace("/[^A-Za-z0-9 ]/", '', $episode->title);
|
||||
$episode->title_search = preg_replace('/[^A-Za-z0-9 ]/', '', $episode->title);
|
||||
$episode->title_jpn = $referenceEpisode->title_jpn ?? $request->input('title_jpn');
|
||||
$episode->slug = "{$hentai->slug}-{$episodeNumber}";
|
||||
$episode->hentai_id = $hentai->id;
|
||||
@@ -96,7 +94,6 @@ class EpisodeService
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function createOrUpdateCover(Request $request, Episode $episode, string $slug, int $episodeNumber): void
|
||||
{
|
||||
if (! $request->hasFile("episodecover{$episodeNumber}")) {
|
||||
@@ -111,7 +108,7 @@ class EpisodeService
|
||||
// Encode and save cover image
|
||||
Image::read($request->file("episodecover{$episodeNumber}")->getRealPath())
|
||||
->cover(268, 394)
|
||||
->encode(new WebpEncoder())
|
||||
->encode(new WebpEncoder)
|
||||
->save(Storage::disk('public')->path($episode->cover_url));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,13 @@ namespace App\Services;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Gallery;
|
||||
use App\Models\Hentai;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
use Intervention\Image\Encoders\WebpEncoder;
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
|
||||
class GalleryService
|
||||
{
|
||||
|
||||
public function createOrUpdateGallery(Request $request, Hentai $hentai, Episode $episode, int $episodeNumber, bool $override = false): void
|
||||
{
|
||||
$galleryInputNumber = $override ? 1 : $episodeNumber;
|
||||
@@ -45,7 +41,7 @@ class GalleryService
|
||||
|
||||
private function createGallery(Hentai $hentai, Episode $episode, int $episodeNumber, int $counter): Gallery
|
||||
{
|
||||
$gallery = new Gallery();
|
||||
$gallery = new Gallery;
|
||||
$gallery->hentai_id = $hentai->id;
|
||||
$gallery->episode_id = $episode->id;
|
||||
$gallery->image_url = "/images/hentai/{$hentai->slug}/gallery-ep-{$episodeNumber}-{$counter}.webp";
|
||||
@@ -59,12 +55,12 @@ class GalleryService
|
||||
{
|
||||
Image::read($sourceImage->getRealPath())
|
||||
->cover(1920, 1080)
|
||||
->encode(new WebpEncoder())
|
||||
->encode(new WebpEncoder)
|
||||
->save(Storage::disk('public')->path($gallery->image_url));
|
||||
|
||||
Image::read($sourceImage->getRealPath())
|
||||
->cover(960, 540)
|
||||
->encode(new WebpEncoder())
|
||||
->encode(new WebpEncoder)
|
||||
->save(Storage::disk('public')->path($gallery->thumbnail_url));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class MatrixRegistrationService
|
||||
{
|
||||
public function registerUser(string $username, string $password)
|
||||
{
|
||||
$server = config('services.matrix.server');
|
||||
$secret = config('services.matrix.shared_secret');
|
||||
|
||||
// Get nonce from Synapse
|
||||
$nonceResponse = Http::get("$server/_synapse/admin/v1/register");
|
||||
|
||||
if (! $nonceResponse->ok()) {
|
||||
throw new \Exception('Could not fetch nonce from Matrix.');
|
||||
}
|
||||
|
||||
$nonce = $nonceResponse->json()['nonce'];
|
||||
|
||||
// Generate MAC
|
||||
$mac = hash_hmac(
|
||||
'sha1',
|
||||
$nonce."\0".
|
||||
$username."\0".
|
||||
$password."\0".
|
||||
'notadmin',
|
||||
$secret
|
||||
);
|
||||
|
||||
// Send registration request
|
||||
$response = Http::post("$server/_synapse/admin/v1/register", [
|
||||
'nonce' => $nonce,
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'admin' => false,
|
||||
'mac' => $mac,
|
||||
]);
|
||||
|
||||
if ($response->failed()) {
|
||||
$error = $response->json()['error'] ?? $response->body();
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
}
|
||||
Generated
+8
-8
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2e359f5cfd56c822e336b74becc9c9d9",
|
||||
"content-hash": "3400111a6254560d548639295422875c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "altcha-org/altcha",
|
||||
@@ -3036,16 +3036,16 @@
|
||||
},
|
||||
{
|
||||
"name": "livewire/livewire",
|
||||
"version": "v3.7.3",
|
||||
"version": "v3.7.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/livewire/livewire.git",
|
||||
"reference": "a5384df9fbd3eaf02e053bc49aabc8ace293fc1c"
|
||||
"reference": "0dc679eb4c8b4470cb12522b5927ef08ca2358bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/a5384df9fbd3eaf02e053bc49aabc8ace293fc1c",
|
||||
"reference": "a5384df9fbd3eaf02e053bc49aabc8ace293fc1c",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/0dc679eb4c8b4470cb12522b5927ef08ca2358bb",
|
||||
"reference": "0dc679eb4c8b4470cb12522b5927ef08ca2358bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3100,7 +3100,7 @@
|
||||
"description": "A front-end framework for Laravel.",
|
||||
"support": {
|
||||
"issues": "https://github.com/livewire/livewire/issues",
|
||||
"source": "https://github.com/livewire/livewire/tree/v3.7.3"
|
||||
"source": "https://github.com/livewire/livewire/tree/v3.7.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3108,7 +3108,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-19T02:00:29+00:00"
|
||||
"time": "2026-02-09T22:49:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maize-tech/laravel-markable",
|
||||
@@ -11584,5 +11584,5 @@
|
||||
"php": "^8.2"
|
||||
},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.9.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ return [
|
||||
'803325441942356059', // Tier-3
|
||||
'803322725576736858', // Tier-2
|
||||
'802270568912519198', // Tier-1
|
||||
'802234830384267315' // admin
|
||||
'802234830384267315', // admin
|
||||
],
|
||||
|
||||
'discord_bot_token' => env('DISCORD_BOT_TOKEN'),
|
||||
|
||||
+2
-2
@@ -30,11 +30,11 @@ return [
|
||||
|
||||
// Asia Fallback (HTTP)
|
||||
'asia_download_domain' => [
|
||||
'https://komako-b-ddl.musume-h.xyz'
|
||||
'https://komako-b-ddl.musume-h.xyz',
|
||||
],
|
||||
|
||||
'asia_stream_domain' => [
|
||||
'https://komako-b-str.musume-h.xyz'
|
||||
'https://komako-b-str.musume-h.xyz',
|
||||
],
|
||||
|
||||
// Free 4k Downloads
|
||||
|
||||
+1
-1
@@ -38,5 +38,5 @@ return [
|
||||
'autoOrientation' => true,
|
||||
'decodeAnimation' => true,
|
||||
'blendingColor' => 'ffffff',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
+2
-2
@@ -149,12 +149,12 @@ return [
|
||||
'title_jpn',
|
||||
'slug',
|
||||
'description',
|
||||
'tags'
|
||||
'tags',
|
||||
],
|
||||
'sortableAttributes' => [
|
||||
'created_at',
|
||||
'release_date',
|
||||
'title'
|
||||
'title',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
+7
-1
@@ -47,5 +47,11 @@ return [
|
||||
'avatar_default_extension' => env('DISCORD_EXTENSION_DEFAULT', 'webp'), // only pick from jpg, png, webp
|
||||
],
|
||||
|
||||
|
||||
/**
|
||||
* Matrix Registration
|
||||
*/
|
||||
'matrix' => [
|
||||
'server' => env('MATRIX_SERVER'),
|
||||
'shared_secret' => env('MATRIX_SHARED_SECRET'),
|
||||
],
|
||||
];
|
||||
|
||||
@@ -263,5 +263,4 @@ return [
|
||||
'confirm_delete_icon' => env('SWEET_ALERT_CONFIRM_DELETE_ICON', 'warning'),
|
||||
'confirm_delete_show_loader_on_confirm' => env('SWEET_ALERT_CONFIRM_DELETE_SHOW_LOADER_ON_CONFIRM', true),
|
||||
|
||||
|
||||
];
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Studios;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,8 @@ use App\Models\Hentai;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
|
||||
@@ -22,8 +22,7 @@ return new class extends Migration
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('gallery', function (Blueprint $table)
|
||||
{
|
||||
Schema::table('gallery', function (Blueprint $table) {
|
||||
$table->dropIndex(['episode_id', 'hentai_id']);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ return new class extends Migration
|
||||
Downloads::create([
|
||||
'episode_id' => $episode->id,
|
||||
'type' => 'FHD',
|
||||
'url' => $episode->download_url
|
||||
'url' => $episode->download_url,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ return new class extends Migration
|
||||
Downloads::create([
|
||||
'episode_id' => $episode->id,
|
||||
'type' => 'FHDi',
|
||||
'url' => $episode->download_url_interpolated
|
||||
'url' => $episode->download_url_interpolated,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ return new class extends Migration
|
||||
Downloads::create([
|
||||
'episode_id' => $episode->id,
|
||||
'type' => 'UHD',
|
||||
'url' => $episode->download_url_4k
|
||||
'url' => $episode->download_url_4k,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
use App\Models\Episode;
|
||||
use App\Models\Hentai;
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ return new class extends Migration
|
||||
|
||||
// Update the title_search field with sanitized titles
|
||||
DB::table('episodes')->update([
|
||||
'title_search' => DB::raw("REGEXP_REPLACE(title, '[^A-Za-z0-9 ]', '')")
|
||||
'title_search' => DB::raw("REGEXP_REPLACE(title, '[^A-Za-z0-9 ]', '')"),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistEpisode;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Downloads;
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
@@ -14,10 +13,10 @@ return new class extends Migration
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
# Delete entries with "#" as URL
|
||||
// Delete entries with "#" as URL
|
||||
Downloads::where('url', '#')->delete();
|
||||
|
||||
# Remove duplicate entries
|
||||
// Remove duplicate entries
|
||||
$duplicates = DB::table('downloads')
|
||||
->select('episode_id', 'type', DB::raw('COUNT(*) as count'))
|
||||
->groupBy('episode_id', 'type')
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
|
||||
@@ -46,7 +46,6 @@ return new class extends Migration
|
||||
$table->rememberToken()->after('password');
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------
|
||||
* Fix Discord Profile Pictures
|
||||
@@ -54,8 +53,7 @@ return new class extends Migration
|
||||
* The oauth package by socialite now returns a full url of the avatar.
|
||||
* Meaning all the old entries have to be fixed.
|
||||
*/
|
||||
foreach (User::whereNotNull('discord_avatar')->get() as $user)
|
||||
{
|
||||
foreach (User::whereNotNull('discord_avatar')->get() as $user) {
|
||||
$isGif = preg_match('/a_.+/m', $user->discord_avatar) === 1;
|
||||
$extension = $isGif ? 'gif' : 'webp';
|
||||
$user->discord_avatar = sprintf('https://cdn.discordapp.com/avatars/%s/%s.%s', $user->id, $user->discord_avatar, $extension);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use App\Models\Playlist;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
|
||||
@@ -69,7 +69,7 @@ return new class extends Migration
|
||||
// Re-Add foreign key constraint and index
|
||||
Schema::table('comments', function (Blueprint $table) {
|
||||
$table->foreign('child_id')->references('id')->on('comments')->onDelete('cascade');
|
||||
$table->index(["commenter_id", "commenter_type"]);
|
||||
$table->index(['commenter_id', 'commenter_type']);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,17 +14,17 @@ return new class extends Migration
|
||||
{
|
||||
// Migrate supporters
|
||||
DB::table('users')->where('is_patreon', 1)->update([
|
||||
'roles' => DB::raw("JSON_ARRAY('supporter')")
|
||||
'roles' => DB::raw("JSON_ARRAY('supporter')"),
|
||||
]);
|
||||
|
||||
// Migrate banned
|
||||
DB::table('users')->where('is_banned', 1)->update([
|
||||
'roles' => DB::raw("JSON_ARRAY('banned')")
|
||||
'roles' => DB::raw("JSON_ARRAY('banned')"),
|
||||
]);
|
||||
|
||||
// Migrate admins
|
||||
DB::table('users')->where('is_admin', 1)->update([
|
||||
'roles' => DB::raw("JSON_ARRAY('admin')")
|
||||
'roles' => DB::raw("JSON_ARRAY('admin')"),
|
||||
]);
|
||||
|
||||
// Drop columns
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('matrix_id')
|
||||
->nullable()
|
||||
->after('discord_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('matrix_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -13,7 +13,7 @@ class DatabaseSeeder extends Seeder
|
||||
public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
DeletedUserSeeder::class
|
||||
DeletedUserSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
|
||||
class DeletedUserSeeder extends Seeder
|
||||
{
|
||||
|
||||
Generated
+6
-5
@@ -4,6 +4,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hstream",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||
"@jellyfin/libass-wasm": "^4.1.1",
|
||||
@@ -1263,14 +1264,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||
"version": "1.13.5",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
|
||||
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"follow-redirects": "^1.15.11",
|
||||
"form-data": "^4.0.5",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -49,6 +49,22 @@
|
||||
<i class="fa-brands fa-discord"></i> {{ __('nav.our-discord-server') }}
|
||||
</x-dropdown-link>
|
||||
|
||||
<x-dropdown-link :href="route('join.matrix')">
|
||||
<div class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
class="icon icon-tabler icons-tabler-outline icon-tabler-brand-matrix pr-1">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M4 3h-1v18h1" />
|
||||
<path d="M20 21h1v-18h-1" />
|
||||
<path d="M7 9v6" />
|
||||
<path d="M12 15v-3.5a2.5 2.5 0 1 0 -5 0v.5" />
|
||||
<path d="M17 15v-3.5a2.5 2.5 0 1 0 -5 0v.5" />
|
||||
</svg>
|
||||
Join our Matrix
|
||||
</div>
|
||||
</x-dropdown-link>
|
||||
|
||||
<x-dropdown-link>
|
||||
<div class="grid grid-cols-2">
|
||||
<p class="cursor-default">{{ __('nav.theme') }}</p>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
@else
|
||||
<p class="text-lg">Episode {{ $episodeNumber }}</p>
|
||||
@endif
|
||||
<p class="text-xs">HEVC MKV {{ $fileSize ?? '' }}</p>
|
||||
<p class="text-xs">{{ $fileExtension }} MKV {{ $fileSize ?? '' }}</p>
|
||||
<p class="text-xs" id="count-{{ $downloadId }}">Downloaded {{ $downloadCount }} times</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -235,6 +235,11 @@
|
||||
'/' .
|
||||
$expire;
|
||||
}
|
||||
|
||||
$fileExtension = "HEVC";
|
||||
if (str_contains($download->url, 'AV1')) {
|
||||
$fileExtension = "AV1";
|
||||
}
|
||||
@endphp
|
||||
<a href="{{ $downloadURL }}" wire:click="clicked({{ $download->id }})"
|
||||
download>
|
||||
@@ -249,7 +254,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex flex-col text-center w-full">
|
||||
<p class="text-xs">HEVC MKV</p>
|
||||
<p class="text-xs">{{ $fileExtension }} MKV</p>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<a href="{{ $dldomains[array_rand($dldomains)] }}/{{ $hdl->getDownloadByType('FHD')->url }}">
|
||||
@php
|
||||
$fileExtension = "HEVC";
|
||||
if (str_contains($hdl->getDownloadByType('FHD')->url, 'AV1')) {
|
||||
$fileExtension = "AV1";
|
||||
}
|
||||
@endphp
|
||||
<button class="group rounded-md shadow bg-rose-600 text-white cursor-pointer flex justify-between items-center overflow-hidden transition-all hover:glow m-1 w-[150px]">
|
||||
<div class="relative w-12 h-12 bg-white bg-opacity-20 text-white flex justify-center items-center transition-all"><svg id="arrow" class="w-4 h-4 transition-all group-hover:-translate-y-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
|
||||
@@ -7,7 +13,7 @@
|
||||
</div>
|
||||
<div class="w-32 text-center">
|
||||
<p class="px-5 text-sm row-span-2">Episode {{ $hdl->episode }}</p>
|
||||
<p class="px-5 text-xs">HEVC MKV</p>
|
||||
<p class="px-5 text-xs">{{ $fileExtension }} MKV</p>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@@ -1,130 +1,101 @@
|
||||
<div class="py-3 relative">
|
||||
<div class="mx-auto sm:px-6 lg:px-8 space-y-6 max-w-[100%] lg:max-w-[90%] xl:max-w-[80%] 2xl:max-w-[90%] relative z-10">
|
||||
<!-- Search Filter -->
|
||||
<div class="p-4 sm:p-8 bg-white/30 dark:bg-neutral-950/40 shadow sm:rounded-lg backdrop-blur relative z-100">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 ">
|
||||
|
||||
<!-- Title -->
|
||||
<div>
|
||||
<label for="live-search"
|
||||
class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
|
||||
<div class="relative right-2 left-0 sm:left-2 transition-all">
|
||||
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
|
||||
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mx-auto max-w-5xl px-4 space-y-6">
|
||||
|
||||
<input wire:model.live.debounce.600ms="commentSearch" type="search" id="live-search"
|
||||
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
|
||||
placeholder="Search comment..." required>
|
||||
<!-- Filters -->
|
||||
<div class="p-4 bg-white/40 dark:bg-neutral-950/40 backdrop-blur rounded-xl shadow flex flex-col sm:flex-row gap-3">
|
||||
|
||||
<div class="absolute right-0 top-[11px]" wire:loading>
|
||||
<svg aria-hidden="true"
|
||||
class="inline w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600"
|
||||
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor" />
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill" />
|
||||
<!-- Search -->
|
||||
<div class="relative flex-1">
|
||||
<input
|
||||
wire:model.live.debounce.500ms="commentSearch"
|
||||
type="search"
|
||||
placeholder="Search comments..."
|
||||
class="w-full pl-10 pr-4 py-3 rounded-lg border-neutral-300 dark:text-neutral-300 bg-white/80 dark:bg-neutral-900/50 dark:border-neutral-700 focus:outline-none focus:ring-2 focus:ring-rose-600 focus:border-rose-700 transition"
|
||||
>
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 pl-3 flex items-center">
|
||||
<svg class="w-4 h-4 text-gray-400 dark:text-gray-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ordering -->
|
||||
<div>
|
||||
<div class="relative right-2 left-0 sm:left-2 transition-all">
|
||||
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
|
||||
<i class="fa-solid fa-sort text-gray-500 dark:text-gray-400"></i>
|
||||
</div>
|
||||
<select wire:model.live="order"
|
||||
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
|
||||
<option value="created_at_desc">Created DESC</option>
|
||||
<option value="created_at_asc">Created ASC</option>
|
||||
<!-- Order -->
|
||||
<select
|
||||
wire:model.live="order"
|
||||
class="px-4 py-3 rounded-lg border-neutral-300 dark:text-gray-300 bg-white/80 dark:bg-neutral-900/50 dark:border-neutral-700 min-w-[128px]"
|
||||
>
|
||||
<option value="created_at_desc">Newest</option>
|
||||
<option value="created_at_asc">Oldest</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="relative pt-5 mx-auto sm:px-6 lg:px-8 space-y-6 text-gray-900 dark:text-white max-w-[100%] lg:max-w-[90%] xl:max-w-[80%] 2xl:max-w-[90%] 2xl:w-[50vw]">
|
||||
<div class="flex flex-col">
|
||||
<div class="overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
<div class="inline-block min-w-full py-2 sm:px-6 lg:px-8">
|
||||
<div class="overflow-hidden">
|
||||
|
||||
<!-- Desktop -->
|
||||
<div class="w-full text-left text-sm font-light">
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="flex bg-white/30 dark:bg-neutral-950/40 backdrop-blur font-medium dark:border-neutral-500 border-b rounded-tl-lg rounded-tr-lg">
|
||||
<!-- Comments -->
|
||||
<div class="space-y-4">
|
||||
@forelse ($comments as $comment)
|
||||
|
||||
<div class="flex-1 px-6 py-4 text-center">Comment</div>
|
||||
</div>
|
||||
|
||||
<!-- Rows -->
|
||||
@foreach ($comments as $comment)
|
||||
<div wire:key="comment-{{ $comment->id }}"
|
||||
class="flex flex-col sm:flex-row items-center border-b bg-white dark:bg-neutral-950 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-neutral-800">
|
||||
|
||||
<!-- Image -->
|
||||
<div class="flex w-fit sm:w-56">
|
||||
@if($comment->commentable_type == \App\Models\Episode::class)
|
||||
@php $episode = \App\Models\Episode::find($comment->commentable_id); @endphp
|
||||
<div class="relative p-1 w-full transition duration-300 ease-in-out md:p-2 md:hover:-translate-y-1 md:hover:scale-110">
|
||||
<a href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
||||
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
|
||||
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
||||
src="{{ $episode->gallery->first()->thumbnail_url }}" />
|
||||
|
||||
<p class="absolute left-1 md:left-2 bottom-1 md:bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
||||
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }}
|
||||
<i class="fa-regular fa-heart"></i> {{ $episode->likeCount() }}
|
||||
<i class="fa-regular fa-comment"></i> {{ $episode->commentCount() }}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
@elseif($comment->commentable_type == \App\Models\Hentai::class)
|
||||
@php
|
||||
$hentai = \App\Models\Hentai::find($comment->commentable_id);
|
||||
$episode = $hentai->episodes->first();
|
||||
$model = $comment->commentable;
|
||||
$episode = $model instanceof \App\Models\Episode
|
||||
? $model
|
||||
: $model->episodes->first();
|
||||
|
||||
$url = route('hentai.index', ['title' => $model->slug]);
|
||||
@endphp
|
||||
<div class="relative p-1 w-full transition duration-300 ease-in-out md:p-2 md:hover:-translate-y-1 md:hover:scale-110">
|
||||
<a href="{{ route('hentai.index', ['title' => $hentai->slug]) }}">
|
||||
<img alt="{{ $episode->title }}" loading="lazy" width="1000"
|
||||
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
||||
src="{{ $episode->gallery->first()->thumbnail_url }}" />
|
||||
|
||||
<a href="{{ $url }}#comment-{{ $comment->id }}"
|
||||
wire:key="comment-{{ $comment->id }}"
|
||||
class="block group">
|
||||
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
<div class="bg-white/40 dark:bg-neutral-950/40 backdrop-blur rounded-xl shadow hover:shadow-lg transition overflow-hidden">
|
||||
|
||||
<div class="flex flex-col sm:flex-row">
|
||||
|
||||
<!-- Thumbnail -->
|
||||
<div class="sm:w-48 shrink-0">
|
||||
<img
|
||||
src="{{ $episode->gallery->first()->thumbnail_url }}"
|
||||
alt=""
|
||||
class="w-full h-40 sm:h-full object-cover"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="flex text-lg flex-1 items-center space-x-2 px-3 py-2 bg-neutral-200 dark:bg-neutral-900 h-[115px] rounded-lg sm:mr-2">
|
||||
<!-- Content -->
|
||||
<div class="flex-1 p-4 flex flex-col justify-between">
|
||||
|
||||
<!-- Comment -->
|
||||
<div class="text-gray-800 dark:text-gray-200 text-sm line-clamp-3">
|
||||
{!! $comment->presenter()->markdownBody() !!}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="space-x-2 sm:mr-2 w-24">
|
||||
<span class="text-gray-500 dark:text-gray-300 font-medium">
|
||||
<!-- Meta -->
|
||||
<div class="flex items-center justify-between mt-3 text-xs text-gray-700 dark:text-gray-400">
|
||||
|
||||
<span>
|
||||
{{ $comment->presenter()->relativeCreatedAt() }}
|
||||
</span>
|
||||
|
||||
<span class="text-rose-600 font-medium group-hover:underline">
|
||||
View comment
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@empty
|
||||
<div class="flex bg-white/40 dark:bg-neutral-950/40 backdrop-blur rounded-xl shadow hover:shadow-lg transition overflow-hidden">
|
||||
<div class="text-gray-800 dark:text-gray-200 text-center w-full p-4">
|
||||
<p class="text-lg">No results</p>
|
||||
<p class="text-sm opacity-70">(╥﹏╥)</p>
|
||||
</div>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div>
|
||||
{{ $comments->links('pagination::tailwind') }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user