mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Allow bulk changing release category
This commit is contained in:
@@ -33,16 +33,40 @@ class AdminReleasesController extends BasePageController
|
||||
|
||||
$meta_title = $title = 'Release List';
|
||||
|
||||
$page = $request->input('page', 1);
|
||||
$releaseList = Release::getReleasesRange($page);
|
||||
$page = (int) $request->input('page', 1);
|
||||
$search = trim((string) $request->input('search', ''));
|
||||
$categoryId = $request->filled('category_id') ? (int) $request->input('category_id') : null;
|
||||
if ($categoryId === -1) {
|
||||
$categoryId = null;
|
||||
}
|
||||
|
||||
$releaseList = Release::getReleasesRange($page, $search !== '' ? $search : null, $categoryId);
|
||||
$releaseList->appends($request->only(['search', 'category_id']));
|
||||
|
||||
return view('admin.releases.index', [
|
||||
'releaselist' => $releaseList,
|
||||
'catlist' => Category::getForSelect(true),
|
||||
'title' => $title,
|
||||
'meta_title' => $meta_title,
|
||||
]);
|
||||
}
|
||||
|
||||
public function bulkCategory(Request $request): RedirectResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'guids' => 'required|array|min:1',
|
||||
'guids.*' => 'string',
|
||||
'categories_id' => 'required|integer|min:1|exists:categories,id',
|
||||
]);
|
||||
|
||||
$count = $this->releaseManagement->bulkUpdateCategory(
|
||||
$validated['guids'],
|
||||
(int) $validated['categories_id'],
|
||||
);
|
||||
|
||||
return back()->with('success', $count.' release(s) re-categorised.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\Foundation\Application|Application|RedirectResponse|Redirector|View
|
||||
*
|
||||
|
||||
+93
-13
@@ -12,6 +12,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Pagination\LengthAwarePaginator as PaginatorLengthAware;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -381,16 +383,99 @@ class Release extends Model
|
||||
* @param int $page The page number to retrieve
|
||||
* @return LengthAwarePaginator|mixed
|
||||
*/
|
||||
public static function getReleasesRange(int $page = 1): mixed
|
||||
public static function getReleasesRange(int $page = 1, ?string $search = null, ?int $categoryId = null): mixed
|
||||
{
|
||||
$expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long'));
|
||||
$cacheKey = md5('releasesRange_'.$page);
|
||||
$releases = Cache::get($cacheKey);
|
||||
if ($releases !== null) {
|
||||
return $releases;
|
||||
$search = $search !== null && $search !== '' ? $search : null;
|
||||
$hasFilters = $search !== null || $categoryId !== null;
|
||||
|
||||
if ($search !== null && Search::isAvailable()) {
|
||||
return self::getReleasesRangeFromSearchIndex($page, $search, $categoryId);
|
||||
}
|
||||
|
||||
$releases = self::query()
|
||||
if (! $hasFilters) {
|
||||
$expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long'));
|
||||
$cacheKey = md5('releasesRange_'.$page);
|
||||
$releases = Cache::get($cacheKey);
|
||||
if ($releases !== null) {
|
||||
return $releases;
|
||||
}
|
||||
}
|
||||
|
||||
$query = self::adminReleaseListQuery();
|
||||
|
||||
if ($search !== null) {
|
||||
$query->where('releases.searchname', 'like', '%'.$search.'%');
|
||||
}
|
||||
|
||||
if ($categoryId !== null) {
|
||||
$query->where('releases.categories_id', $categoryId);
|
||||
}
|
||||
|
||||
$releases = $query->paginate(config('nntmux.items_per_page'), ['*'], 'page', $page);
|
||||
|
||||
if (! $hasFilters) {
|
||||
$expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long'));
|
||||
Cache::put(md5('releasesRange_'.$page), $releases, $expiresAt);
|
||||
}
|
||||
|
||||
return $releases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PaginatorLengthAware<int, mixed>
|
||||
*/
|
||||
private static function getReleasesRangeFromSearchIndex(int $page, string $search, ?int $categoryId): PaginatorLengthAware
|
||||
{
|
||||
$perPage = (int) config('nntmux.items_per_page');
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$criteria = [
|
||||
'phrases' => ['searchname' => $search],
|
||||
'category_ids' => $categoryId !== null ? [$categoryId] : null,
|
||||
'sort_field' => 'postdate_ts',
|
||||
'sort_dir' => 'desc',
|
||||
'try_fuzzy' => true,
|
||||
];
|
||||
|
||||
$filtered = Search::searchReleasesFiltered($criteria, $perPage, $offset);
|
||||
$ids = array_map(static fn (int|string $id): int => (int) $id, $filtered['ids'] ?? []);
|
||||
$total = (int) ($filtered['total'] ?? 0);
|
||||
|
||||
if ($ids === []) {
|
||||
return new PaginatorLengthAware([], 0, $perPage, $page, [
|
||||
'path' => Paginator::resolveCurrentPath(),
|
||||
'pageName' => 'page',
|
||||
]);
|
||||
}
|
||||
|
||||
$idList = implode(',', $ids);
|
||||
$rows = self::adminReleaseListQuery()
|
||||
->whereIn('releases.id', $ids)
|
||||
->orderByRaw('FIELD(releases.id, '.$idList.')')
|
||||
->get();
|
||||
|
||||
return new PaginatorLengthAware($rows, $total, $perPage, $page, [
|
||||
'path' => Paginator::resolveCurrentPath(),
|
||||
'pageName' => 'page',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cached admin release-list pages after bulk updates.
|
||||
*/
|
||||
public static function clearAdminReleasesRangeCache(int $maxPages = 25): void
|
||||
{
|
||||
for ($page = 1; $page <= $maxPages; $page++) {
|
||||
Cache::forget(md5('releasesRange_'.$page));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<Release>
|
||||
*/
|
||||
private static function adminReleaseListQuery(): Builder
|
||||
{
|
||||
return self::query()
|
||||
->select(
|
||||
[
|
||||
'releases.id',
|
||||
@@ -409,12 +494,7 @@ class Release extends Model
|
||||
)
|
||||
->leftJoin('categories as c', 'c.id', '=', 'releases.categories_id')
|
||||
->leftJoin('root_categories as cp', 'cp.id', '=', 'c.root_categories_id')
|
||||
->orderByDesc('releases.postdate')
|
||||
->paginate(config('nntmux.items_per_page'), ['*'], 'page', $page);
|
||||
|
||||
Cache::put($cacheKey, $releases, $expiresAt);
|
||||
|
||||
return $releases;
|
||||
->orderByDesc('releases.postdate');
|
||||
}
|
||||
|
||||
public static function getByGuid(mixed $guid): mixed
|
||||
|
||||
@@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Service for managing releases (delete, update, export).
|
||||
@@ -111,6 +112,60 @@ class ReleaseManagementService
|
||||
return $updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $guids
|
||||
*/
|
||||
public function bulkUpdateCategory(array $guids, int $categoryId): int
|
||||
{
|
||||
$guids = array_values(array_filter($guids));
|
||||
if ($guids === [] || $categoryId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$updated = 0;
|
||||
|
||||
DB::transaction(function () use ($guids, $categoryId, &$updated): void {
|
||||
$releaseIds = Release::query()
|
||||
->whereIn('guid', $guids)
|
||||
->pluck('id');
|
||||
|
||||
$updated = Release::query()
|
||||
->whereIn('guid', $guids)
|
||||
->update(['categories_id' => $categoryId, 'iscategorized' => 1]);
|
||||
|
||||
if ($updated > 0) {
|
||||
$this->syncReleasesToSearchIndex($releaseIds);
|
||||
Release::clearAdminReleasesRangeCache();
|
||||
}
|
||||
});
|
||||
|
||||
return $updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-index releases after query-builder updates that bypass {@see ReleaseObserver}.
|
||||
*
|
||||
* @param Collection<int, int|string>|iterable<int|string> $releaseIds
|
||||
*/
|
||||
private function syncReleasesToSearchIndex(iterable $releaseIds): void
|
||||
{
|
||||
foreach ($releaseIds as $releaseId) {
|
||||
$intId = (int) $releaseId;
|
||||
if ($intId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Search::updateRelease($intId);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('ReleaseManagementService: Failed to sync release to search index after category change', [
|
||||
'release_id' => $intId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Release[]|Builder[]|\Illuminate\Database\Eloquent\Collection<int, mixed>|\Illuminate\Database\Query\Builder[]|Collection<int, mixed>
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user