Skip to content

feat(metadata): add RanobeDB as metadata provider#185

Merged
neonsolstice merged 2 commits into
bookorbit:mainfrom
chrismansell26:BO-114-ranobedb-metadata-source
Jun 1, 2026
Merged

feat(metadata): add RanobeDB as metadata provider#185
neonsolstice merged 2 commits into
bookorbit:mainfrom
chrismansell26:BO-114-ranobedb-metadata-source

Conversation

@chrismansell26

@chrismansell26 chrismansell26 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

This PR introduces a brand new metadata provider to fetch book metadata from RanobeDB.

Key additions and features:

  • Adds ranobedb.client to interact with the RanobeDB API.
  • Implements ranobedb.mapper to map RanobeDB data to our internal metadata schema, which includes:
    • Mapping native Japanese titles to subtitles.
    • Using the original language as a fallback for the language field.
    • Parsing specific release dates to determine the original publication year.
  • Registers RanobeDB as an available provider in the preferences UI (ProviderConfigPanel).
  • Updates the database schema with a new migration to add a ranobedb_id column.
  • Removes the bookId override when manually performing a metadata search in the UI.

Closes #114

How did you test this?

  • Added comprehensive unit tests for the new RanobeDB client, mapper, and provider (ranobedb.client.test.ts, ranobedb.mapper.test.ts, ranobedb.provider.test.ts).
  • Verified all lint checks and test suites pass locally.
  • Spun up the app locally and confirmed the RanobeDB provider can be enabled/disabled in the metadata preferences.
  • Manually tested fetching metadata for a light novel via the UI to ensure mapping works seamlessly.

Screenshots

Screenshot 2026-05-31 at 9 52 26 PM

Anything non-obvious in the diff?

  • Included a database migration (0011_add_ranobedb_id.sql) to track the ranobedb_id for books linked to this provider.
  • You'll notice provider-colors.ts has been updated with a new color mapped to RanobeDB for the UI badges.

Checklist

  • I've read through my own diff
  • This PR was discussed in an issue and has maintainer approval (for new features)
  • Lint and tests pass locally
  • No unintended files included (build artifacts, .env, personal configs)

Summary by CodeRabbit

  • New Features
    • RanobeDB added as a new metadata source for book search and lookup.
    • RanobeDB IDs can be viewed and edited in book metadata and included in refresh/preview flows.
    • RanobeDB appears in metadata preferences (can be enabled/disabled) and in provider badges/labels.
  • Database
    • Schema updated to store RanobeDB IDs.

Introduces a new metadata provider to fetch book metadata from RanobeDB.

- Adds `ranobedb.client` to interact with the RanobeDB API.
- Implements `ranobedb.mapper` to map RanobeDB data to the internal metadata schema.
  - Maps native Japanese titles to subtitles.
  - Uses the original language as a fallback for the language field.
  - Parses specific release dates to determine the original publication year.
- Registers RanobeDB as an available provider in the preferences UI.
- Adds `ranobedb_id` to the metadata database schema.
- Removes the `bookId` override when manually performing a metadata search in the UI.

Closes bookorbit#114
@github-actions github-actions Bot added server Changes affecting server-side code, APIs, or backend behavior. client Changes affecting the client application or frontend behavior. packages labels Jun 1, 2026
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 51e9afd9-ecb6-41aa-b611-25235d558e00

📥 Commits

Reviewing files that changed from the base of the PR and between c41fe46 and 39680cc.

📒 Files selected for processing (1)
  • client/src/lib/__tests__/provider-colors.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • client/src/lib/tests/provider-colors.spec.ts

📝 Walkthrough

Walkthrough

This PR adds RanobeDB as a complete metadata source to BookOrbit: types and DB schema, a rate-limited RanobeDB HTTP client, mapping logic to MetadataCandidate, a RanobeDB provider implementation registered in DI, orchestration to persist and surface ranobedbId, provider configuration defaults, and client UI/editor integration for managing ranobedbId.

Changes

RanobeDB Metadata Provider Integration

Layer / File(s) Summary
Type Contracts and Database Schema
packages/types/src/metadata-fetch.ts, packages/types/src/metadata-lock.ts, packages/types/src/metadata-preferences.ts, server/src/db/schema/metadata.ts, server/src/db/migrations/...
Adds RANOBEDB provider key, lock field ranobedbId, provider preference entry, and a ranobedb_id varchar(50) column with migration journal entry.
Client-side Metadata Editor Integration
client/src/features/book/components/detail/tabs/EditMetadataTab.vue, client/src/features/book/composables/useMetadata*.ts, client/src/features/book/components/detail/tabs/MetadataSearchDrawer.vue, client/src/features/settings/metadata-preferences/components/ProviderConfigPanel.vue, client/src/lib/provider-colors.ts, client/src/lib/__tests__/provider-colors.spec.ts
Integrates ranobedbId into the editable form, diff/patch system, refresh preview mapping, and provider config UI; adds RanobeDB color and tests for provider color/labels/styles.
Server Provider Registration & Configuration
server/src/modules/metadata-fetch/metadata-fetch.module.ts, server/src/modules/metadata-preferences/provider-config.service.ts, server/src/modules/metadata-fetch/providers/provider-constants.ts
Register RanobeDbClient and RanobeDbProvider in DI, add ranobedb defaults (enabled: false), provider label, merge logic, limits, and per-provider delays; update module tests to include the provider.
RanobeDB HTTP Client
server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.client.ts, server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.client.test.ts
Add rate-limited HTTP client with timeout/abort handling, search() and fetchBook() APIs, and tests covering success, non-OK responses, errors, and throttle propagation.
RanobeDB Types and Mapper
server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.types.ts, server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.ts, server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.test.ts
Introduce RanobeDB API types and mapping logic (parseDateInt, mapRanobeDbBook) to produce MetadataCandidate with language-aware selection and deduplication; comprehensive mapper tests included.
RanobeDB Provider Service
server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.provider.ts, server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.provider.test.ts
Implement IdentifiableProvider for RanobeDB with search() (client search + book fetch + map) and lookupById() (numeric validation, safe-int checks); includes tests for disabled state, ID parsing edge cases, and signal propagation.
Metadata Fetch Repo & Service Integration
server/src/modules/metadata-fetch/metadata-fetch.repository.ts, server/src/modules/metadata-fetch/metadata-fetch.service.ts, server/src/modules/book-metadata-fetch/book-metadata-fetch-orchestrator.service.ts
Select and map ranobedb_id from stored metadata rows into provider-id maps and persist resolved ranobedbId during orchestration.
Book Service and Locking
server/src/modules/book/dto/update-book-metadata.dto.ts, server/src/modules/book/book.service.ts, server/src/modules/book-metadata-lock/book-metadata-lock.service.ts
Add ranobedbId to Update DTO, collect/apply/persist ranobedbId in book service flows, and extend lock service to filter/skip ranobedbId updates when locked.
Test Fixture Updates
server/src/modules/metadata-fetch/providers/*/*.provider.test.ts, server/src/modules/cover/providers/*
Extend many provider test fixtures to include a ranobedb: { enabled: false } entry and update related module tests to expect RanobeDB registration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

server, client, packages

Poem

🐰 A rabbit hops through databases new,
RanobeDB joins the crew!
From Tokyo light novels bright,
Metadata flows—what a sight!
Search, fetch, map, and persist with glee,
BookOrbit's expanding family! 📚✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(metadata): add RanobeDB as metadata provider' clearly and specifically summarizes the main change, accurately reflecting the introduction of RanobeDB as a new metadata provider.
Description check ✅ Passed The PR description is comprehensive, covering what was done, how it was tested, and specific implementation details. However, it does not explicitly address all template sections like checklist items verification.
Linked Issues check ✅ Passed The PR fully implements the RanobeDB metadata provider feature requested in issue #114, including the API client, mapper, UI integration, database migration, and comprehensive testing.
Out of Scope Changes check ✅ Passed All changes are directly related to adding RanobeDB as a metadata provider. Test file updates for existing providers are scoped infrastructure changes to support the new provider type. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.ts (2)

82-92: 💤 Low value

Consider adding inline comment for publishedYear fallback logic.

The cascading fallback logic (release date → c_release_dates[olang] → c_release_date) is sophisticated and would benefit from a brief comment explaining the priority order.

📝 Suggested inline comment
 function resolvePublishedYear(book: RanobeDbBook, release: RanobeDbRelease | undefined): number | undefined {
+  // Priority: English release date > original language release date > computed release date
   if (release?.release_date) {
     const year = parseDateInt(release.release_date);
     if (year) return year;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.ts`
around lines 82 - 92, Add a brief inline comment inside resolvePublishedYear
explaining the fallback priority used to determine publishedYear: first prefer
release.release_date (parsed via parseDateInt), then fall back to
book.c_release_dates keyed by book.olang (if present), and finally use
book.c_release_date; mention that parseDateInt may return undefined so each
branch checks and returns the first valid year. Place this comment above the
conditional block so future readers understand the cascade and rationale.

8-12: 💤 Low value

Consider adding inline comment for year range validation.

The year range 1000-2200 is reasonable for publication dates, but a brief comment explaining this validation would improve readability for future maintainers.

📝 Suggested inline comment
 export function parseDateInt(value: number | null | undefined): number | undefined {
   if (!value) return undefined;
   const year = Math.floor(value / 10000);
+  // Validate year is within plausible publication range
   return year >= 1000 && year <= 2200 ? year : undefined;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.ts`
around lines 8 - 12, The parseDateInt function uses a hard-coded year check
(year >= 1000 && year <= 2200) with no explanation; add a brief inline comment
above the conditional in parseDateInt explaining that the 1000–2200 range is
chosen to filter out malformed timestamps while covering expected publication
dates (e.g., historical works and near-future entries), and note that the upper
bound is conservative and can be adjusted if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/src/lib/__tests__/provider-colors.spec.ts`:
- Around line 5-67: Tests expect ranobedb color '`#E53935`' but the implementation
(PROVIDER_HEX.ranobedb) returns '`#a78cff`', causing failures in getProviderColor,
providerBadgeStyle, and providerChipStyle specs; fix by either (A) updating the
tests to expect '`#a78cff`' wherever '`#E53935`' is asserted (in getProviderColor
spec, providerBadgeStyle assertion of style.color, and providerChipStyle
assertions) or (B) changing the source constant PROVIDER_HEX.ranobedb in
provider-colors.ts to '`#E53935`' so getProviderColor, providerBadgeStyle, and
providerChipStyle return the expected value—pick one approach and make
consistent updates across the referenced symbols (getProviderColor,
providerBadgeStyle, providerChipStyle, PROVIDER_HEX.ranobedb).

In `@client/src/lib/provider-colors.ts`:
- Line 13: The exported color for the provider "ranobedb" in provider-colors.ts
conflicts with the spec; update the mapping so getProviderColor('ranobedb')
returns the expected value (`#E53935`). Locate the color map (the object exported
from provider-colors.ts) and change the value for the "ranobedb" key from
'`#a78cff`' to '`#E53935`', then run the provider-colors.spec.ts tests to confirm
badge/chip style checks pass.

In
`@server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.test.ts`:
- Around line 114-117: The test in ranobedb.mapper.test.ts duplicates the same
out-of-range assertion for parseDateInt (both
expect(parseDateInt(9990101)).toBeUndefined()); update the second assertion to
use a different out-of-range value (e.g.,
expect(parseDateInt(22010101)).toBeUndefined()) so you validate both low/high
year boundaries for the parseDateInt function.

---

Nitpick comments:
In `@server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.ts`:
- Around line 82-92: Add a brief inline comment inside resolvePublishedYear
explaining the fallback priority used to determine publishedYear: first prefer
release.release_date (parsed via parseDateInt), then fall back to
book.c_release_dates keyed by book.olang (if present), and finally use
book.c_release_date; mention that parseDateInt may return undefined so each
branch checks and returns the first valid year. Place this comment above the
conditional block so future readers understand the cascade and rationale.
- Around line 8-12: The parseDateInt function uses a hard-coded year check (year
>= 1000 && year <= 2200) with no explanation; add a brief inline comment above
the conditional in parseDateInt explaining that the 1000–2200 range is chosen to
filter out malformed timestamps while covering expected publication dates (e.g.,
historical works and near-future entries), and note that the upper bound is
conservative and can be adjusted if needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 58c13a15-0720-4b2e-8a74-bb6388bdb0b9

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe1165 and c41fe46.

📒 Files selected for processing (44)
  • client/src/features/book/components/detail/tabs/EditMetadataTab.vue
  • client/src/features/book/components/detail/tabs/MetadataSearchDrawer.vue
  • client/src/features/book/composables/useMetadataDiff.ts
  • client/src/features/book/composables/useMetadataEditor.ts
  • client/src/features/book/composables/useRefreshMetadata.ts
  • client/src/features/settings/metadata-preferences/components/ProviderConfigPanel.vue
  • client/src/lib/__tests__/provider-colors.spec.ts
  • client/src/lib/provider-colors.ts
  • packages/types/src/metadata-fetch.ts
  • packages/types/src/metadata-lock.ts
  • packages/types/src/metadata-preferences.ts
  • server/src/db/migrations/0011_add_ranobedb_id.sql
  • server/src/db/migrations/meta/0011_snapshot.json
  • server/src/db/migrations/meta/_journal.json
  • server/src/db/schema/metadata.ts
  • server/src/modules/book-metadata-fetch/book-metadata-fetch-orchestrator.service.ts
  • server/src/modules/book-metadata-lock/book-metadata-lock.service.ts
  • server/src/modules/book/book.service.ts
  • server/src/modules/book/dto/update-book-metadata.dto.ts
  • server/src/modules/cover/providers/itunes-cover-provider.test.ts
  • server/src/modules/metadata-fetch/metadata-fetch.module.test.ts
  • server/src/modules/metadata-fetch/metadata-fetch.module.ts
  • server/src/modules/metadata-fetch/metadata-fetch.repository.ts
  • server/src/modules/metadata-fetch/metadata-fetch.service.ts
  • server/src/modules/metadata-fetch/providers/amazon/amazon.provider.test.ts
  • server/src/modules/metadata-fetch/providers/audible/audible.provider.test.ts
  • server/src/modules/metadata-fetch/providers/audnexus/audnexus.provider.test.ts
  • server/src/modules/metadata-fetch/providers/comicvine/comicvine.provider.test.ts
  • server/src/modules/metadata-fetch/providers/goodreads/goodreads.provider.test.ts
  • server/src/modules/metadata-fetch/providers/google/google.provider.test.ts
  • server/src/modules/metadata-fetch/providers/hardcover/hardcover.provider.test.ts
  • server/src/modules/metadata-fetch/providers/itunes/itunes.provider.test.ts
  • server/src/modules/metadata-fetch/providers/open-library/open-library.provider.test.ts
  • server/src/modules/metadata-fetch/providers/provider-constants.ts
  • server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.client.test.ts
  • server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.client.ts
  • server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.test.ts
  • server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.ts
  • server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.provider.test.ts
  • server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.provider.ts
  • server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.types.ts
  • server/src/modules/metadata-preferences/dto/update-provider-config.dto.ts
  • server/src/modules/metadata-preferences/provider-config.service.test.ts
  • server/src/modules/metadata-preferences/provider-config.service.ts

Comment thread client/src/lib/__tests__/provider-colors.spec.ts
audible: '#FF8A00',
audnexus: '#FF5ADD',
comicvine: '#ffdb0f',
ranobedb: '#a78cff',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Color mismatch with the new spec — tests will fail.

provider-colors.spec.ts asserts getProviderColor('ranobedb') returns #E53935 (and checks the same value in badge/chip styles), but this source defines #a78cff. These conflict, so the new test suite will fail. Reconcile the two on the intended color (the consolidated note is on the spec file).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/lib/provider-colors.ts` at line 13, The exported color for the
provider "ranobedb" in provider-colors.ts conflicts with the spec; update the
mapping so getProviderColor('ranobedb') returns the expected value (`#E53935`).
Locate the color map (the object exported from provider-colors.ts) and change
the value for the "ranobedb" key from '`#a78cff`' to '`#E53935`', then run the
provider-colors.spec.ts tests to confirm badge/chip style checks pass.

Comment on lines +114 to +117
it('returns undefined for years out of plausible range', () => {
expect(parseDateInt(9990101)).toBeUndefined();
expect(parseDateInt(9990101)).toBeUndefined();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Duplicate test assertion on line 116.

Line 116 repeats the same assertion as line 115. The second assertion should test a different out-of-range value to ensure both boundaries are validated (e.g., year too high: 22010101).

🔧 Proposed fix
   it('returns undefined for years out of plausible range', () => {
     expect(parseDateInt(9990101)).toBeUndefined();
-    expect(parseDateInt(9990101)).toBeUndefined();
+    expect(parseDateInt(22010101)).toBeUndefined();
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('returns undefined for years out of plausible range', () => {
expect(parseDateInt(9990101)).toBeUndefined();
expect(parseDateInt(9990101)).toBeUndefined();
});
it('returns undefined for years out of plausible range', () => {
expect(parseDateInt(9990101)).toBeUndefined();
expect(parseDateInt(22010101)).toBeUndefined();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/metadata-fetch/providers/ranobedb/ranobedb.mapper.test.ts`
around lines 114 - 117, The test in ranobedb.mapper.test.ts duplicates the same
out-of-range assertion for parseDateInt (both
expect(parseDateInt(9990101)).toBeUndefined()); update the second assertion to
use a different out-of-range value (e.g.,
expect(parseDateInt(22010101)).toBeUndefined()) so you validate both low/high
year boundaries for the parseDateInt function.

@neonsolstice

Copy link
Copy Markdown
Collaborator

This looks great, I ran end-to-end tests and everything passed.

@neonsolstice
neonsolstice merged commit 200d885 into bookorbit:main Jun 1, 2026
22 checks passed
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.8.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions github-actions Bot added the released Issue or PR is included in a released version. label Jun 1, 2026
Logan-Selley pushed a commit to Logan-Selley/bookorbit that referenced this pull request Jun 2, 2026
* feat(metadata): add RanobeDB metadata provider

Introduces a new metadata provider to fetch book metadata from RanobeDB.

- Adds `ranobedb.client` to interact with the RanobeDB API.
- Implements `ranobedb.mapper` to map RanobeDB data to the internal metadata schema.
  - Maps native Japanese titles to subtitles.
  - Uses the original language as a fallback for the language field.
  - Parses specific release dates to determine the original publication year.
- Registers RanobeDB as an available provider in the preferences UI.
- Adds `ranobedb_id` to the metadata database schema.
- Removes the `bookId` override when manually performing a metadata search in the UI.

Closes bookorbit#114
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client Changes affecting the client application or frontend behavior. packages released Issue or PR is included in a released version. server Changes affecting server-side code, APIs, or backend behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] RanobeDB as a Metadata Source

2 participants