Commit Graph

128 Commits

Author SHA1 Message Date
kikootwo 5b4aa3fa15 Add data-migration tracking; prevent subtitle dedup
Track and run run-once SQL data migrations: entrypoint now checks _data_migrations before executing each prisma data-migration file, records successful runs, and skips already-applied scripts. Adds a Prisma DataMigration model mapped to _data_migrations and a new reset-works-table.sql migration to clear work tables for a dedup rebuild. Also improves dedup logic: extractSubtitle and subtitle-compatibility checks are added so series entries like "Series: Book A" vs "Series: Book B" are not collapsed, with accompanying unit tests for extraction and behavior.
2026-03-05 16:45:56 -05:00
kikootwo 859a331012 Run data migrations; use search title for ranking
Add an entrypoint step to execute idempotent SQL data migrations (prisma db execute) from prisma/data-migrations/*.sql so fixes that prisma db push doesn't handle are applied on startup. Add normalize-local-usernames.sql to normalize local users' plex_username and plex_id to lowercase. Update interactive search and search-indexers processor to prefer the user-provided/custom search title (searchTitle / effectiveSearchTitle) when ranking torrents and adjust debug logs to show the ranking title alongside the audiobook title/author for clearer diagnostics.
2026-03-05 15:02:59 -05:00
kikootwo 09e1a0db3a Use .gl for Anna's Archive; add manual-import test
Replace default Anna's Archive base URL from https://annas-archive.li to https://annas-archive.gl across docs, UI components, API routes, processors, services, and tests. Add comprehensive tests for the admin manual-import API route and enhance the manual-import route to fetch missing ASIN details from Audnexus and create audiobook records with proper error handling and logging. Update related test expectations and FlareSolverr test usages to reflect the new default URL.
2026-03-05 12:20:00 -05:00
kikootwo 832a8ad00b Merge branch 'main' of https://github.com/kikootwo/ReadMeABook 2026-03-05 11:31:49 -05:00
kikootwo cc8e106a2b Add per-user home sections & unified Audible cache
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.
2026-03-05 11:30:39 -05:00
kikootwo 6025ac200a Merge branch 'main' into feature/hardover-shelves 2026-03-04 23:16:08 -05:00
Michael Borohovski 81813dc625 Fix token UI success handling, fetch error surfacing, and docs key stability 2026-03-04 16:53:11 -08:00
kikootwo efb4f64014 Count errors and skip shelf on token decrypt fail
When decrypting a user's API token fails, increment stats.errors and continue to the next shelf instead of proceeding. This ensures failed decryptions are tracked in metrics and prevents attempting to fetch data with an invalid token.
2026-03-04 15:53:50 -05:00
kikootwo 95917715b1 Remove redundant id field from JWT payloads
Drop the duplicated `id` alias from JWT payloads and related token generation across auth providers and endpoints. The TokenPayload interface no longer includes `id`; middleware now derives `user.id` from `sub` when attaching the authenticated user to requests. Update tests accordingly. This reduces redundancy and ensures the canonical user identifier is `sub`.
2026-03-04 15:36:28 -05:00
kikootwo a50fbc721e Add useApiTokens hook and refactor token UI
Introduce a shared useApiTokens hook to centralize API token CRUD and UI state (fetch, create, delete, copy, formatting). Refactor ApiTab and ApiTokensSection to consume the hook and remove duplicated logic. Add getInstanceUrl utility for client origin used in curl examples. Include an id alias in TokenPayload and add id into generated JWTs across auth routes and providers; update tests accordingly. Improve auth middleware typing and add debug logging around lastUsedAt updates. Add admin logging when creating a token with a role that differs from the target user's role.
2026-03-04 15:18:48 -05:00
kikootwo d6eca611fc Add API tokens management, docs & UI
Introduce full API token support: add a Prisma migration to create api_tokens table and indexes; add types, constants and a generateApiToken utility (hashed token + prefix). Update admin and user token routes to use the generator, enforce per-user active token caps, and integrate rate-limit checks. Add an interactive API docs page with TokenInput, EndpointCard and ResponseViewer components, plus a protected page route. Improve confirmation UX with an accessible ConfirmDialog (focus trap, Escape to close, animations) and wire confirm flows into admin/profile token sections; also update ConfirmModal to accept node messages. Add dialog CSS animations and enhance clipboard error handling. Update related middleware, utils and tests to reflect changes.
2026-03-04 14:51:23 -05:00
kikootwo 85977d123c Merge branch 'main' into feature/per-user-api-tokens 2026-03-04 13:26:57 -05:00
kikootwo 441724c378 Normalize local usernames to lowercase
Normalize local account usernames by trimming and lowercasing across the stack. Added a Prisma migration to lowercase existing plex_username and rewrite local plex_id values for non-deleted accounts. Updated LocalAuthProvider, admin login route, and setup completion to use normalized usernames when looking up, creating, and storing users (including plexId `local-{username}`). Added/updated tests to assert case-insensitive lookups, storage of lowercased usernames/plexIds, and duplicate username rejection.
2026-03-04 12:47:09 -05:00
kikootwo d0ce485bdc Enrich audiobook metadata from Audnexus
Query Audnexus (Audible) to backfill missing metadata during manual imports and file organization. Adds getAudibleService imports and calls to fetch audiobook details by ASIN, then backfills series, seriesPart, seriesAsin, year (from releaseDate) and narrator when missing and updates the DB. Failures are non-fatal and logged; logs were added to surface enrichment steps. Also uses the resolved series/seriesPart when building organization metadata.
2026-03-04 12:19:37 -05:00
kikootwo c29cfa3a07 Fix token handling, modal behavior, and pagination
Multiple fixes and improvements:

- src/app/api/user/hardcover-shelves/[id]/route.ts: Make token testing more robust by using the existing shelf.apiToken when no new token is provided, attempt decryption only when needed, and gracefully fall back on decryption errors.
- src/components/ui/AddShelfModal.tsx: Simplify token handling by passing the trimmed token directly to addHardcover (remove client-side 'Bearer ' stripping).
- src/components/ui/ManageShelfModal.tsx: Stabilize form reset effect by depending on shelf?.id to avoid unnecessary re-renders when the shelf object changes identity.
- src/components/ui/Modal.tsx: Simplify modal rendering by removing the mounted state and createPortal usage, cleaning up imports and rendering directly.
- src/lib/services/hardcover-api.service.ts: Add a logger, introduce a MAX_PAGES cap and page counters to prevent unbounded pagination loops, and log/break when the API returns errors during pagination.

These changes improve reliability (token handling and pagination safety), reduce unnecessary renders, and simplify modal lifecycle.
2026-03-04 10:55:37 -05:00
kikootwo 7f706e806f Use hardcover-api service with pagination
Replace the old hardcover sync usage with a new hardcover-api.service implementation that adds types, a reusable extractBooks helper, and paginated GraphQL queries (limit/offset) to fully fetch status and list books. Update API route import to use the new service. Fix ManageShelfModal to initialize rssUrl/listId as empty strings. Update tests to mock the new service and add encryption format helper mocking.
2026-03-04 10:28:52 -05:00
kikootwo 338331d006 Add Hardcover shelf sync & unify book mappings
Introduce Hardcover provider support and consolidate per-provider book mapping tables into a unified BookMapping model. Adds two Prisma migrations (add_hardcover_shelves, unify_book_mappings), new backend services (hardcover-api, shelf-sync-core), and provider-specific sync logic and API routes for hardcover shelves with token/list validation. Frontend: new HardcoverForm component, refactor AddShelfModal to support Hardcover, hook updates, and small UI/accessibility tweaks. Also add documentation for Goodreads and Hardcover sync flows and update tests to cover scheduler/prisma helpers.
2026-03-04 10:11:19 -05:00
kikootwo 6ca2e964e8 Merge branch 'main' into feature/hardover-shelves 2026-03-03 22:23:41 -05:00
kikootwo 1d1aaa7ff3 Merge pull request #126 from brombomb/hardcover-api
Unified Reading Shelves & Hardcover Integration
2026-03-03 22:13:32 -05:00
kikootwo cbf02d3e24 Add watched series/authors feature
Introduce watched lists for series and authors end-to-end.

- Add DB migration to create watched_series and watched_authors tables with indexes and foreign keys.
- Implement API routes: GET/POST for listing/adding and DELETE by id for both /api/user/watched-series and /api/user/watched-authors. Validation, ownership checks, and immediate targeted job triggers are included.
- Add client hooks (useWatchedSeries, useWatchedAuthors) with add/delete helpers and SWR revalidation.
- Add UI components: WatchButton (toggle/confirm) and WatchedListsSection for profile display and removal UX.
- Add processor (check-watched-lists.processor) and service (watched-lists.service) to scrape Audible, deduplicate, check library ownership, and auto-create requests; supports targeted checks for newly watched items.
- Include tests for the watched-lists service.

These changes implement the watched-lists feature to let users watch series/authors and have the system automatically detect and request new releases.
2026-03-03 21:57:38 -05:00
Michael Borohovski 04b6a2c135 Harden API token auth for deleted users and add route rate limiting 2026-03-03 15:16:03 -08:00
Michael Borohovski 61b183542c Add per-user API tokens with admin override support
- Add userId field to ApiToken schema (the user identity the token acts as)
- Auth middleware resolves token identity via userId instead of createdById
- New /api/user/api-tokens routes for self-service token management
- Admin /api/admin/api-tokens routes support userId and role overrides
- API Tokens section on profile page for all users
- Admin API tab shows all tokens with user/role selectors
2026-03-03 12:23:57 -08:00
Rob Walsh ae4a73144d cleanup dep jobs 2026-03-03 13:20:28 -07:00
Rob Walsh c57d0c1492 Add a manage shelf modal 2026-03-03 13:16:23 -07:00
Rob Walsh 8f8387abff token encryption 2026-03-03 12:19:12 -07:00
Rob Walsh 4ae68d01de Encrypt Hardcover Api Token and fix failing tests 2026-03-03 11:51:38 -07:00
Rob Walsh 225ef8c919 Fix import to limit to 100, and scope to me for personal lists 2026-03-03 11:38:30 -07:00
kikootwo 610873af6b Add works table and ASIN deduping
Add persistent cross-ASIN "works" mapping and client-side deduplication to improve library matching. Introduces a Prisma migration and models (Work, WorkAsin) plus src/lib/services/works.service for persisting dedup groups, seeding ASINs at request time, and sibling lookup. Adds a deduplication utility (deduplicate-audiobooks) that normalizes titles/narrators, compares durations, and returns grouping metadata; API routes (search, author, series) now deduplicate results before enrichment and fire-and-forget persist groups. Adds sibling-ASIN expansion into audiobook matcher and expands getAvailableAsins accordingly. Extracts runtime parsing into a shared parse-runtime util and updates audible scrapers/services to use it. Includes unit tests for dedup logic and works service and updates test Prisma mocks.
2026-03-03 13:31:46 -05:00
kikootwo ff80d995c5 Add hideAvailable filter and unified pagination
Add support for hiding audiobooks that are already available by introducing a hideAvailable query flag and excluding matching ASINs at the DB level. Implemented getAvailableAsins() in audiobook-matcher to gather ASINs from the library and completed requests, and wired it into the popular and new-releases API routes to apply a notIn filter. Propagated the hideAvailable flag through useAudiobooks so client requests include the parameter, and adjusted the homepage to reset pagination when the flag changes. Replaced two StickyPagination instances with a new UnifiedPagination component (new file) that provides a single context-aware floating paginator which tracks the dominant section and allows switching between Popular and New Releases. Also removed client-side filtering in favor of server-side exclusion and made small imports/cleanup in page.tsx.
2026-03-03 12:36:03 -05:00
kikootwo b559835390 Merge branch 'main' of https://github.com/kikootwo/ReadMeABook 2026-03-02 17:05:28 -05:00
kikootwo d25a6ebf79 Add custom search terms & retry download (admin)
Add support for per-request custom search terms and an admin retry-download flow.

- DB/schema: add custom_search_terms column via Prisma migration and schema update.
- Admin UI: new AdjustSearchTermsModal component and UI badges to show custom search status; RequestActionsDropdown and RecentRequestsTable updated to surface adjust/retry actions.
- API: new PATCH /api/admin/requests/[id]/search-terms to set/clear custom terms (optionally trigger a new search) and new POST /api/admin/requests/[id]/retry-download to resume monitoring or re-add downloads using DownloadHistory metadata.
- Behavior: interactive search now prefers customSearchTerms when present; manual import exposes cleanupSource option to organize job; admin requests listing returns downloadAttempts and customSearchTerms.
- UX: add SectionToolbar, LoadMoreBar and HideAvailableToggle components and wire hide-available preference across home, search, author and series pages; authors/series endpoints/page handlers gain pagination metadata.
- Misc: add connection-errors util and update related processors/services and tests to cover the new flows.

These changes enable admins to override search terms per request, trigger searches from the admin UI, and retry failed downloads more robustly.
2026-03-02 17:05:21 -05:00
Rob Walsh 7891e31893 Undo formatting noise 2026-03-02 13:58:11 -07:00
Rob Walsh bff74446fe Fix gemini key 2026-03-02 13:48:49 -07:00
Rob Walsh b940ad39f9 Better UX for Custom Lists 2026-03-02 13:45:16 -07:00
Rob Walsh 038c92e49f Add gemini bookdate support 2026-02-28 22:55:59 -07:00
Rob Walsh 3861d07cf4 Remove boy scout formatting changes 2026-02-27 20:36:17 -07:00
Rob Walsh 41d45d1210 Refactor shelves UI and jobs 2026-02-27 15:46:10 -07:00
Rob Walsh cfe780c6f0 Hardcover API support 2026-02-27 15:10:27 -07:00
kikootwo edc56bc457 Add manual-import and download-access features
Introduce manual import workflow and download permission support. Adds a Prisma migration and schema field (users.download_access) to track per-user download access, and updates admin UI to toggle global and per-user download access. Implements new APIs: filesystem browse, manual-import endpoint, download-access settings, audiobook download-status, and on-demand download-token generation. Adds frontend components for manual import and related tests, plus documentation for the manual-import feature and the documentation-agent prompt. Key files: prisma/migrations/20260212000000_add_download_access_permission/migration.sql, prisma/schema.prisma, src/app/api/admin/filesystem/browse/route.ts, src/app/api/admin/manual-import/route.ts, src/app/api/admin/settings/download-access/route.ts, src/app/api/requests/[id]/download-token/route.ts, src/app/api/audiobooks/[asin]/download-status/route.ts, and updated admin users pages/components and permissions util.
2026-02-27 12:15:23 -05:00
razzamatazm f0b9bd2688 Fix organizer collisions for nested duplicate track names 2026-02-26 17:23:04 -08:00
razzamatazm e1629ce516 Address PR review: dedicated download secret, shared constants, strip filePath, streaming zip
- jwt.ts: Use JWT_DOWNLOAD_SECRET instead of JWT_SECRET for download tokens
- audio-formats.ts: Add EBOOK_EXTENSIONS export alongside AUDIO_EXTENSIONS
- request-statuses.ts: New shared COMPLETED_STATUSES constant used across requests API, download route, and RequestCard
- requests/route.ts: Import COMPLETED_STATUSES; strip filePath from audiobook in API response
- download/route.ts: Import format/status constants; add archiver dependency and replace adm-zip with streaming archiver for multi-file zips
- RequestCard.tsx: Use shared COMPLETED_STATUSES constant
2026-02-26 16:20:37 -08:00
razzamatazm 1006a04337 Add direct file download links to completed requests
Embeds a signed JWT download token (30-day expiry) in the requests API
response so users can download completed audiobook/ebook files directly
from the UI or by sharing the URL to apps like BookPlayer — no session
cookie required.

- jwt.ts: add generateDownloadToken / verifyDownloadToken helpers
- api/requests: append downloadUrl to completed requests with a filePath
- api/requests/[id]/download: new token-authenticated streaming endpoint;
  serves single files directly or zips multi-file audiobooks with adm-zip
- RequestCard: add Download link in the actions area for completed requests
2026-02-26 11:33:32 -08:00
kikootwo 1b0a80052d Use content_path and add savePath/path-wait
Always use qBittorrent's content_path as the canonical downloadPath and expose savePath on DownloadInfo instead of reconstructing paths from save_path + basename. Add path-waiting logic to the monitor: track consecutive pathWaitCount polls, re-queue the monitor with exponential-ish backoff while content_path remains outside save_path (to handle TempPathEnabled races), and give up after a configurable max attempts. Extend the MonitorDownload payload and JobQueue APIs to carry pathWaitCount. Organize-files processor now attempts to refresh the stored downloadPath from the download client and updates downloadHistory if the client reports a different path (applying path mapping). Update tests to reflect the new behavior and expectations.
2026-02-26 12:45:24 -05:00
kikootwo dbea15a34f Use content_path basename for finished torrents
When a torrent is finished (seeding/completed), build the download path from save_path combined with the basename of content_path instead of using torrent.name or the full content_path. This fixes a race with qBittorrent's TempPathEnabled (where content_path may still point to the temp dir) and addresses cases where the displayed torrent.name differs from the actual root folder/filename on disk. Added/updated tests to cover the TempPathEnabled race, name-mismatch scenarios, empty content_path fallback, and single-file torrents.
2026-02-26 09:45:23 -05:00
kikootwo 03f82d4841 File rename templates & admin torrent approval
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.
2026-02-25 09:47:57 -05:00
kikootwo 33c2265e56 Use save_path for completed/seeding torrents
Resolve downloadPath using save_path for finished torrents to avoid TempPathEnabled race conditions where content_path can point to a stale temp location. Compute status once, treat 'seeding'/'completed' as finished, and prefer path.join(save_path, name) for those states while still using content_path (or falling back to save_path) for active downloads. Added tests covering multiple qBittorrent states (seeding/stalledUP/pausedUP/stoppedUP/forcedUP/queuedUP/downloading and empty content_path) and imported path in tests.
2026-02-24 02:03:20 -05:00
kikootwo b15a472bab Centralize download client timeout constant
Add DOWNLOAD_CLIENT_TIMEOUT (60000ms) in src/lib/constants/download-timeouts.ts and replace hardcoded 60000ms timeouts across Deluge, Prowlarr, qBittorrent and Transmission integrations. This centralizes the download/API timeout (gives headroom for indexers that enforce ~30s waits) and makes future adjustments easier without changing behavior.
2026-02-24 01:09:58 -05:00
kikootwo 3c680f2f38 Merge pull request #102 from Kikipeuk/ygg_timeout2
Extend the default timeout to add a torrent (Qbit, Transmission, Deluge)
2026-02-24 00:56:37 -05:00
kikootwo 40d5363dc4 Fix French stopWords spacing and region name
Trim whitespace in the French stopWords array (add missing space after comma) to keep formatting consistent, and rename AUDIBLE_REGIONS.fr.name from "French" to "France" to better reflect the region label used for the Audible configuration.
2026-02-24 00:51:55 -05:00
root 328fd8392b ygg_timeout2 2026-02-21 14:30:51 +01:00