Add first-class ebook request support and UI

Implements first-class ebook requests with their own type, parent-child relationship to audiobook requests, and separate status flow. Updates database schema and migrations to support 'type' and 'parentRequestId' fields on requests. Adds processors and job types for ebook search and direct HTTP download from Anna's Archive, with FlareSolverr integration for Cloudflare bypass. Enhances admin UI tables and request actions to display and manage ebook requests, including orange badge and source links. Updates documentation to reflect new ebook support, configuration, and behavior.
This commit is contained in:
kikootwo
2026-01-30 15:59:25 -05:00
parent 2cda6decbe
commit 590f089733
37 changed files with 2810 additions and 666 deletions
@@ -18,6 +18,7 @@ interface RecentRequest {
title: string;
author: string;
status: string;
type?: 'audiobook' | 'ebook';
user: string;
createdAt: Date;
completedAt: Date | null;
@@ -237,7 +238,7 @@ export function RecentRequestsTable({ requests, ebookSidecarEnabled = false }: R
<thead className="bg-gray-50 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Audiobook
Request
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
User
@@ -264,8 +265,21 @@ export function RecentRequestsTable({ requests, ebookSidecarEnabled = false }: R
>
<td className="px-6 py-4">
<div>
<div className="text-sm font-medium text-gray-900 dark:text-gray-100">
{request.title}
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
{request.title}
</span>
{request.type === 'ebook' && (
<span
className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full"
style={{ backgroundColor: '#f16f1920', color: '#f16f19' }}
>
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 4.804A7.968 7.968 0 005.5 4c-1.255 0-2.443.29-3.5.804v10A7.969 7.969 0 015.5 14c1.669 0 3.218.51 4.5 1.385A7.962 7.962 0 0114.5 14c1.255 0 2.443.29 3.5.804v-10A7.968 7.968 0 0014.5 4c-1.255 0-2.443.29-3.5.804V12a1 1 0 11-2 0V4.804z" />
</svg>
Ebook
</span>
)}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
{request.author}
@@ -280,7 +294,9 @@ export function RecentRequestsTable({ requests, ebookSidecarEnabled = false }: R
<td className="px-6 py-4 text-sm text-gray-900 dark:text-gray-100">
{request.user}
</td>
<td className="px-6 py-4">{getStatusBadge(request.status)}</td>
<td className="px-6 py-4">
{getStatusBadge(request.status)}
</td>
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{formatDistanceToNow(new Date(request.createdAt), { addSuffix: true })}
</td>
@@ -298,6 +314,7 @@ export function RecentRequestsTable({ requests, ebookSidecarEnabled = false }: R
title: request.title,
author: request.author,
status: request.status,
type: request.type,
torrentUrl: request.torrentUrl,
}}
onDelete={handleDeleteClick}