mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 12:50:09 +00:00
Add multi-source ebook support and per-indexer categories
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.
This commit is contained in:
@@ -4,13 +4,18 @@
|
||||
*
|
||||
* Groups indexers by their category configuration to minimize API calls.
|
||||
* Indexers with identical categories are grouped together for a single search.
|
||||
* Supports separate audiobook and ebook category configurations per indexer.
|
||||
*/
|
||||
|
||||
export type CategoryType = 'audiobook' | 'ebook';
|
||||
|
||||
export interface IndexerConfig {
|
||||
id: number;
|
||||
name: string;
|
||||
priority?: number;
|
||||
categories?: number[];
|
||||
audiobookCategories?: number[]; // Categories for audiobook searches
|
||||
ebookCategories?: number[]; // Categories for ebook searches
|
||||
categories?: number[]; // Legacy field for backwards compatibility
|
||||
[key: string]: any; // Allow other properties
|
||||
}
|
||||
|
||||
@@ -20,38 +25,70 @@ export interface IndexerGroup {
|
||||
indexers: IndexerConfig[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate categories from an indexer based on the category type.
|
||||
*
|
||||
* @param indexer - The indexer configuration
|
||||
* @param type - The category type ('audiobook' or 'ebook')
|
||||
* @returns Array of category IDs
|
||||
*/
|
||||
export function getCategoriesForType(indexer: IndexerConfig, type: CategoryType): number[] {
|
||||
if (type === 'ebook') {
|
||||
return indexer.ebookCategories && indexer.ebookCategories.length > 0
|
||||
? indexer.ebookCategories
|
||||
: [7020]; // Default ebook category
|
||||
}
|
||||
|
||||
// Audiobook - check new field first, then legacy field
|
||||
if (indexer.audiobookCategories && indexer.audiobookCategories.length > 0) {
|
||||
return indexer.audiobookCategories;
|
||||
}
|
||||
if (indexer.categories && indexer.categories.length > 0) {
|
||||
return indexer.categories; // Legacy fallback
|
||||
}
|
||||
return [3030]; // Default audiobook category
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups indexers by their category configuration.
|
||||
* Indexers with identical category arrays are grouped together.
|
||||
*
|
||||
* @param indexers - Array of indexer configurations
|
||||
* @param type - The category type to group by ('audiobook' or 'ebook')
|
||||
* @returns Array of groups, each containing indexers with matching categories
|
||||
*
|
||||
* @example
|
||||
* const indexers = [
|
||||
* { id: 1, categories: [3030] },
|
||||
* { id: 2, categories: [3030] },
|
||||
* { id: 3, categories: [3030, 3010] },
|
||||
* { id: 1, audiobookCategories: [3030], ebookCategories: [7020] },
|
||||
* { id: 2, audiobookCategories: [3030], ebookCategories: [7020] },
|
||||
* { id: 3, audiobookCategories: [3030, 3010], ebookCategories: [7020] },
|
||||
* ];
|
||||
*
|
||||
* const groups = groupIndexersByCategories(indexers);
|
||||
* const audiobookGroups = groupIndexersByCategories(indexers, 'audiobook');
|
||||
* // Result:
|
||||
* // [
|
||||
* // { categories: [3030], indexerIds: [1, 2], indexers: [...] },
|
||||
* // { categories: [3030, 3010], indexerIds: [3], indexers: [...] }
|
||||
* // ]
|
||||
*
|
||||
* const ebookGroups = groupIndexersByCategories(indexers, 'ebook');
|
||||
* // Result:
|
||||
* // [
|
||||
* // { categories: [7020], indexerIds: [1, 2, 3], indexers: [...] }
|
||||
* // ]
|
||||
*/
|
||||
export function groupIndexersByCategories(indexers: IndexerConfig[]): IndexerGroup[] {
|
||||
export function groupIndexersByCategories(
|
||||
indexers: IndexerConfig[],
|
||||
type: CategoryType = 'audiobook'
|
||||
): IndexerGroup[] {
|
||||
// Map to track unique category combinations
|
||||
// Key: sorted category IDs as string (e.g., "3030,3010")
|
||||
// Value: array of indexers with those categories
|
||||
const groupMap = new Map<string, IndexerConfig[]>();
|
||||
|
||||
for (const indexer of indexers) {
|
||||
// Get categories, default to [3030] (audiobooks) if not specified
|
||||
const categories = indexer.categories && indexer.categories.length > 0
|
||||
? indexer.categories
|
||||
: [3030];
|
||||
// Get categories for the specified type
|
||||
const categories = getCategoriesForType(indexer, type);
|
||||
|
||||
// Sort categories to ensure consistent grouping
|
||||
// [3030, 3010] and [3010, 3030] should be the same group
|
||||
|
||||
@@ -36,7 +36,11 @@ export const TORRENT_CATEGORIES: TorrentCategory[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const DEFAULT_CATEGORIES = [3030]; // Audio/Audiobook
|
||||
export const DEFAULT_AUDIOBOOK_CATEGORIES = [3030]; // Audio/Audiobook
|
||||
export const DEFAULT_EBOOK_CATEGORIES = [7020]; // Books/EBook
|
||||
|
||||
// Legacy alias for backwards compatibility
|
||||
export const DEFAULT_CATEGORIES = DEFAULT_AUDIOBOOK_CATEGORIES;
|
||||
|
||||
/**
|
||||
* Get all child IDs for a parent category
|
||||
|
||||
Reference in New Issue
Block a user