mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-07-14 08:41:11 +00:00
Add skip-unreleased auto-search feature
Introduce an indexer-wide option to skip automatic searches for books with future release dates (config key: `indexer.skip_unreleased`, default ON). Adds a GET/PUT admin API for indexer options, a UI toggle on the Indexers settings tab (persisted on save), and persistence of a request-level releaseDate in the Prisma schema. Adds a new request status `awaiting_release` and wires it through constants, UI components (StatusBadge, RequestCard, RecentRequestsTable, Audiobook card/modal, RequestActions), API request flows (bookdate swipe, request creation, manual search, request PATCHs, request listing groups), and services. Implements a pure release-date utility (isUnreleased / shouldSkipAutoSearch) and updates background processors: monitor-rss-feeds (skip matches but do not mutate status), retry-missing-torrents (drives bidirectional transitions between awaiting_search and awaiting_release and queues searches when appropriate), and request-creator/bookdate swipe (gate initial auto-search). Adds tests for the swipe gate and other related test updates. Logs transitions and gate decisions for observability.
This commit is contained in:
@@ -34,7 +34,7 @@ const getStatusConfig = (audiobook: Audiobook) => {
|
||||
return { type: 'processing', label: 'Processing', color: 'amber' };
|
||||
}
|
||||
|
||||
const pendingStatuses = ['pending', 'awaiting_search', 'searching', 'awaiting_approval'];
|
||||
const pendingStatuses = ['pending', 'awaiting_search', 'awaiting_release', 'searching', 'awaiting_approval'];
|
||||
if (audiobook.requestStatus && pendingStatuses.includes(audiobook.requestStatus)) {
|
||||
return { type: 'pending', label: 'Requested', color: 'blue' };
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ const getStatusInfo = (isAvailable: boolean, requestStatus: string | null, reque
|
||||
return { type: 'processing', label: 'Processing', canRequest: false };
|
||||
}
|
||||
|
||||
const pendingStatuses = ['pending', 'awaiting_search', 'searching', 'awaiting_approval'];
|
||||
const pendingStatuses = ['pending', 'awaiting_search', 'awaiting_release', 'searching', 'awaiting_approval'];
|
||||
if (requestStatus && pendingStatuses.includes(requestStatus)) {
|
||||
const label = requestStatus === 'awaiting_approval'
|
||||
? requestedByUsername ? `Pending Approval (${requestedByUsername})` : 'Pending Approval'
|
||||
|
||||
@@ -27,6 +27,7 @@ interface RequestCardProps {
|
||||
updatedAt: string;
|
||||
completedAt?: string;
|
||||
downloadAvailable?: boolean;
|
||||
releaseDate?: string | Date | null;
|
||||
audiobook: {
|
||||
id: string;
|
||||
audibleAsin?: string;
|
||||
@@ -58,6 +59,18 @@ export function RequestCard({ request, showActions = true }: RequestCardProps) {
|
||||
const isActive = ['searching', 'downloading', 'processing'].includes(request.status);
|
||||
const isFailed = request.status === 'failed';
|
||||
|
||||
const releaseDateLabel = React.useMemo(() => {
|
||||
if (request.status !== 'awaiting_release' || !request.releaseDate) return null;
|
||||
const parsed = new Date(request.releaseDate);
|
||||
if (Number.isNaN(parsed.getTime())) return null;
|
||||
return parsed.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
});
|
||||
}, [request.status, request.releaseDate]);
|
||||
|
||||
const handleConfirmCancel = async () => {
|
||||
try {
|
||||
await cancelRequest(request.id);
|
||||
@@ -150,6 +163,11 @@ export function RequestCard({ request, showActions = true }: RequestCardProps) {
|
||||
{/* Status Badge and Type Badge */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<StatusBadge status={request.status} progress={request.progress} />
|
||||
{releaseDateLabel && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Releases {releaseDateLabel}
|
||||
</span>
|
||||
)}
|
||||
{isEbook && (
|
||||
<span
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full"
|
||||
|
||||
@@ -68,6 +68,10 @@ export function StatusBadge({ status, progress, className }: StatusBadgeProps) {
|
||||
label: 'Pending Approval',
|
||||
color: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
|
||||
},
|
||||
awaiting_release: {
|
||||
label: 'Awaiting Release',
|
||||
color: 'bg-teal-100 text-teal-800 dark:bg-teal-900 dark:text-teal-200',
|
||||
},
|
||||
denied: {
|
||||
label: 'Request Denied',
|
||||
color: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
|
||||
|
||||
Reference in New Issue
Block a user