Introduce a per-request release blocklist to auto-block permanently failing releases and provide admin management. Changes include:
- Database: add BlockedRelease model (blocked_releases) to Prisma schema with unique (requestId, releaseKey) and indexes; documented in backend database docs.
- Service & utils: new blocklist.service, release-key and filter helpers for normalization and matching; processors updated to emit auto-blocks (monitor-download, organize-files, search processors, RSS).
- HTTP API: add admin endpoints GET/DELETE /api/admin/blocklist, DELETE /api/admin/blocklist/[id], and GET /api/admin/blocklist/by-request/[requestId].
- Admin UI: new /admin/blocklist page and numerous React components (toolbar, filters, table, rows, pagination, skeleton, chips, date picker) with URL-driven state hook and per-row unblock UX.
- Tests: add unit/integration tests for service, routes, utils, and updated processor tests.
The blocklist is idempotent (upsert), filters search results before ranking (interactive search shows badges only), and admin-only APIs require auth. This commit wires docs, API, DB, frontend and tests for the new feature.
Extract LoadingSpinner and ApprovalActionButtons components and replace duplicated approve/search/deny button blocks with the new ApprovalActionButtons to reduce duplication and centralize behavior/styles. Remove the inline LoadingSpinner in PendingApprovalSection, add an aria-label to the details button, and update the details modal's adminActions to use ApprovalActionButtons with callbacks that handle approval/denial/search and close modals as needed. Improves DRY, maintainability, and consistency of loading state handling.
Adds an info icon button (top-right of each card) in the Requests
Awaiting Approval section. Clicking it opens AudiobookDetailsModal
with full book details (cover, description, narrator, series, genres,
etc.) and embeds the Approve / Search / Deny action buttons so admins
can review and act without navigating away from the admin panel.
Implementation:
- AudiobookDetailsModal: adds optional `adminActions` prop rendered as
a second row inside the existing sticky action bar
- admin/page.tsx: adds detailsAsin/detailsRequestId state, info button
per card (conditional on audibleAsin presence), and AudiobookDetailsModal
wired with admin action buttons matching the card button behaviour
- Documentation updated: request-approval.md, components.md, TABLEOFCONTENTS.md
Closes#157
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce a Bulk Import feature for admins to scan server folders, match discovered audiobook folders against Audible, review matches, and queue batch imports.
What changed:
- Added documentation: documentation/features/bulk-import.md and TABLEOFCONTENTS update.
- Backend: SSE scan endpoint (POST /api/admin/bulk-import/scan) streams discovery and matching events; execute endpoint (POST /api/admin/bulk-import/execute) validates paths, creates/resolves audiobook & request records, and queues organize_files jobs. Both endpoints enforce admin-only access and validate allowed root directories (download_dir, media_dir, /bookdrop).
- Frontend: Modal wizard and steps for folder selection, scan progress, and match review (BulkImportWizard + ScanFolderStep, ScanProgressStep, MatchReviewStep + shared types).
- Utilities: bulk-import-scanner for folder discovery and ffprobe metadata extraction; shared types for scanned books/events.
- UI: Added Bulk Import quick action to admin dashboard (src/app/admin/page.tsx).
Key details:
- Audible searches are rate-limited (≈1.5s) and matching results include library/request status checks.
- Reuses existing organize_files job queue and manual-import pipeline; no new database tables introduced (state is ephemeral during the wizard).
- Includes error handling, path normalization, and security checks for allowed directories.
This commit wires frontend, backend, and docs together to provide an admin-only multi-step bulk import workflow.
Introduce per-user configurable home page sections and a unified Audible cache/category model. Adds Prisma models (UserHomeSection, AudibleCacheCategory) and migrations to create tables and remove legacy popular/new_release flags; updates schema.prisma accordingly. Add API routes for user home sections, live Audible categories, and category-based audiobook listing, and refactor popular/new-releases/covers routes to read from AudibleCacheCategory. Frontend: new HomeSection component, HomeSectionConfigModal, useHomeSections hook, and homepage changes to render dynamic sections plus image fallback to a placeholder SVG. Also add placeholder_cover.svg and tests for home sections and the audible refresh processor.
Add support for admin-driven interactive torrent selection and a file rename/template feature. Integrates an InteractiveTorrentSearchModal into the pending-approval admin UI, adds an admin approve flow that accepts an admin-selected torrent, and surfaces user/admin-selected torrent details in the UI. Introduces fileRenameEnabled and fileRenameTemplate settings (API + UI), persists them to configuration, and clears related caches. Pass renameConfig through the organize/organizeEbook flows and implement renaming in the FileOrganizer (single/multi-file handling). Enhance path-template utilities with conditional block resolution, filename-template validation, mock filename previews, and a buildRenamedFilename helper. Update tests to cover conditional templates and filename preview behavior.
Introduce user-reported-issues and Goodreads shelf sync features and wire them into notifications. Adds Prisma migrations and schema changes (ReportedIssue, GoodreadsShelf, GoodreadsBookMapping), API endpoints for reporting (POST /audiobooks/[asin]/report-issue) and admin management (list, resolve/dismiss, replace), and an admin UI section to view/dismiss/replace reported issues. Adds a new notification event (issue_reported) with updates to notification schemas, docs and provider handling, plus a notification-events constants file. Refactors request creation to use createRequestForUser service, adds a Goodreads sync processor/service/hooks/UI modals, a scrape-resilience util, and related tests and minor integration updates.
Introduce ROOTLESS_CONTAINER env to opt out of gosu (replace /proc uid_map detection) and update entrypoint messaging; adjust app-start.sh and redis-start.sh to skip gosu when ROOTLESS_CONTAINER=true and warn on UID/GID mismatch only when applicable. Backend: include audiobook audibleAsin in admin requests response (mapped to asin) and pass baseUrl through test-flaresolverr endpoint to the FlareSolverr tester. Frontend: RecentRequestsTable and RequestActionsDropdown now surface asin, accept/passthrough annasArchiveBaseUrl, and add a "View Details" flow using AudiobookDetailsModal; admin page passes ebook baseUrl from settings. InteractiveTorrentSearchModal refactor: improved UX/UI, keyboard handling, portal/modal mounting, skeleton/loading states, formatting helpers, and richer result display. Tests updated to match changes.
Introduce ebook-sidecar support: add new API routes for ebook workflows (ebook-status, fetch-ebook, interactive-search-ebook, select-ebook) that handle searching, selection, request creation, approval, and download routing (Anna's Archive direct downloads vs indexer downloads).
Update admin approval flow to understand request.type (audiobook | ebook), handle pre-selected ebook torrents (including special handling for Anna's Archive with direct download jobs and download history), and enqueue ebook-specific search/download jobs.
Frontend changes: show request type badge in admin pending approvals and augment AudiobookDetailsModal to query ebook status, start fetch/interactive ebook searches, and surface toast notifications. Also include new request lifecycle handling (retryable/active statuses, approval logic, creating audiobook records for Plex-imported books) and ranking/normalization logic for interactive ebook search results.
Other: various plumbing to integrate config checks, job queue calls, and download history storage for ebook downloads.
Refactor RecentRequestsTable to manage filters client-side and sync them to the browser URL. Removed next/navigation hooks (useRouter, usePathname, useSearchParams) and added getInitialParams + local useState for page, pageSize, status, userId, sortBy, sortOrder and a debouncedSearch. Added keepPreviousData to SWR to avoid layout shifts, implemented history.replaceState to shallow-sync URL when filters change, and a popstate handler to support back/forward navigation. Consolidated filter updates via updateFilter, added page reset behavior on filter/search changes, and improved search debouncing logic. Also removed Suspense usage and import around RecentRequestsTable in admin/page.tsx.
Add a paginated Admin Requests API and fully refactor the admin requests UI to support filtering, sorting, pagination, and URL state.
- New API: src/app/api/admin/requests/route.ts implements paginated, searchable, filterable, and sortable request listing with proper relation includes and pagination metadata.
- Frontend: RecentRequestsTable rewritten to fetch via SWR (authenticatedFetcher), read/write URL query params, debounce search, support status/user filters, sortable columns, page size selector, and full pagination UI; added loading/error states and toast feedback for actions.
- Admin page updated to use Suspense and the new RecentRequestsTable (component now fetches its own data).
- Settings: deprecated single download-client PUT route now maps updates into the new multi-client format (download_clients JSON), logs deprecation, and invalidates download client manager; settings GET now reads multi-client config for backward compatibility.
- Processors: monitor-download and retry-failed-imports updated to use the download-client-manager and new PathMappingConfig shape for path mapping logic.
- Minor API/schema updates: request-with-torrent schema extended (indexerId, infoUrl, protocol) and setup complete no longer writes legacy path keys.
- Tests updated to reflect API and processor changes.
This change centralizes request management on the server, modernizes the UI for large datasets, and migrates download client settings toward a multi-client configuration while keeping backward compatibility.
Introduces granular toggles for Anna's Archive and Indexer Search as ebook sources, updates settings UI to a three-section layout, and documents the new configuration. Adds per-indexer category configuration with separate tabs for audiobooks and ebooks, updates API routes and types for new settings, and ensures legacy config migration. Indexer grouping and file organization logic now support the new category structure and ebook source toggles.
Implements admin approval workflow for user requests with global and per-user auto-approve controls. Adds new request statuses ('awaiting_approval', 'denied'), related API endpoints, and UI for pending approvals. Introduces configurable audiobook organization path template with validation and preview in settings, updates database schema and migrations for new fields.
Introduces an API endpoint to trigger e-book downloads for completed requests, with admin UI integration in RecentRequestsTable and RequestActionsDropdown. Updates the admin dashboard to detect e-book sidecar feature availability from settings. Enhances torrent search result handling with info URLs, improves ranking algorithm normalization, and refines interactive search to show all results without threshold filtering. Also allows nullable ratings in request schemas.