Skip to content

feat(xtracter): pixivFANBOX対応を追加 - #561

Merged
hmjn023 merged 5 commits into
developfrom
feat/xtracter-fanbox-support
Jul 1, 2026
Merged

hmjn023 merged 5 commits into
developfrom
feat/xtracter-fanbox-support

Conversation

@hmjn023

@hmjn023 hmjn023 commented Jun 30, 2026 •

Copy link
Copy Markdown
Owner

概要

xtracterからpixivFANBOXの記事画像を保存・POSTできるようにします。

変更内容

  • FANBOX記事画像へのDL・POSTボタン追加
  • CDN画像URLと元記事URLのメタデータ登録
  • platform単位のAuthorアカウント管理と既存Twitterデータ移行
  • JSON・バックアップ・LanceDB同期へのplatform反映
  • accountId_platform_postId_assetId形式の統一ファイル名

検証

  • 対象ワークスペースのtypecheck・lint
  • xtracter build
  • core unit tests
  • server関連unit・integration tests

Summary by CodeRabbit

  • 新機能

    • 作者に外部アカウント情報を持たせられるようになり、Twitter、pixivFANBOX、Danbooru に対応しました。
    • xtracter が pixivFANBOX 投稿ページでもメディアを検出し、取得ボタンを表示するようになりました。
    • メディアファイル名がプラットフォーム付きの新形式になりました。
  • 改善

    • バックアップ、復元、同期処理で作者アカウント情報を保持できるようになりました。
    • 検索・絞り込みUIの候補表示がより安定し、選択済み項目の扱いが改善されました。

Add FANBOX image extraction, platform-scoped author accounts, metadata persistence across JSON and LanceDB, and deterministic media filenames.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8bc53d7c-2281-4bb4-92f1-c87a912dab20

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/xtracter-fanbox-support

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for linking authors to multiple platform identities (such as Twitter, pixiv-fanbox, and Danbooru) by adding the author_accounts table and updating the database schema, backup services, and the Chrome extension. Feedback on these changes highlights a potential bug in AuthorRepository.create that could lead to duplicate author records, a performance issue with N+1 queries in findOrCreateBulk, and minor logic improvements in the newly added FANBOX content script regarding redundant regex matching and incorrect parsing of 'www' as a creator ID.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/db/src/repositories/author-repository.ts Outdated
Comment thread packages/db/src/repositories/author-repository.ts
Comment thread apps/xtracter/src/content/fanbox.ts Outdated
Comment thread apps/xtracter/src/content/fanbox.ts Outdated
@hmjn023

hmjn023 commented Jun 30, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

本プルリクエストでは、著者(Author)に対して「Twitter」「pixiv-fanbox」「danbooru」といった複数のプラットフォームアカウントを紐付ける機能が追加されました。これに伴い、データベーススキーマに author_accounts テーブルが導入され、バックアップ処理、インポート/エクスポート、ファイル名生成規則、および検索フィルターUIがプラットフォーム情報を考慮するように更新されています。また、Chrome拡張機能(xtracter)にpixivFANBOX対応が追加されました。レビューフィードバックでは、UIの Combobox コンポーネントにおいて optionValue をIDに変更したことで選択状態の同期や onChange の挙動が壊れる問題、FANBOXでの画像遅延読み込み(lazy-load)への考慮不足、innerText によるリフロー発生の懸念、および AuthorRepository.create で名前が空の場合に型定義違反(undefined の返却)が発生するバグが指摘されています。

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/ui/src/pro-search-builder.tsx
Comment thread apps/server/src/components/media/pro-search-builder.tsx
Comment thread apps/xtracter/src/content/fanbox.ts Outdated
Comment thread apps/xtracter/src/content/fanbox.ts Outdated
Comment thread packages/db/src/repositories/author-repository.ts

@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: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/ui/src/pro-search-builder.tsx (1)

481-486: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

作者の選択値も ID ベースに寄せることを検討してください。

optionValue は item.id ですが、保存値と復元は name のままです。同名の別 platform/account 作者があると、選択後に最初の同名作者へ復元されます。

🤖 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 `@packages/ui/src/pro-search-builder.tsx` around lines 481 - 486, The author
selector is mixing ID-based options with name-based persistence, which can
restore the wrong item when names collide. Update the selection flow in
pro-search-builder.tsx around the autocomplete value handling so the saved
criterion value uses the same ID returned by optionValue, and change the value
lookup to match by id rather than name. Keep the fix consistent with the
existing author selection logic in the relevant autocomplete component and props
wiring so selection and restoration both use the same unique identifier.
apps/server/src/components/media/pro-search-builder.tsx (1)

481-486: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

作者の選択値も ID ベースに寄せることを検討してください。

optionValue は item.id ですが、保存値と復元は name のままです。同名の別 platform/account 作者があると、選択後に最初の同名作者へ復元されます。

🤖 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 `@apps/server/src/components/media/pro-search-builder.tsx` around lines 481 -
486, The author selector is mixing ID-based options with name-based persistence,
which can restore the wrong item when duplicate names exist. Update the
selection flow in pro-search-builder.tsx so the criterion value is stored and
restored by the same ID used by optionValue, and adjust the value lookup in the
relevant select/autocomplete component to resolve by item.id instead of
item.name. Make sure the surrounding props and any setter logic tied to this
field use the same identifier consistently.
🧹 Nitpick comments (1)
apps/server/src/components/media/pro-search-builder.tsx (1)

466-478: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

キャストではなく型付き Combobox と type guard にしてください。

ここは package 側と同様に Combobox<RelationOption> と "accountId" in item で扱えます。不要な as Author / as { name: string } は避けてください。

修正例
-					<Combobox
+					<Combobox<RelationOption>
...
-									? props.criterion.target === "author"
-										? getAuthorLabel(item as Author)
-										: (item as { name: string }).name
+									? "accountId" in item
+										? getAuthorLabel(item)
+										: item.name

As per coding guidelines, **/*.{ts,tsx}: In application code, do not use any, unnecessary unknown, as unknown as ..., or as any; prefer public types, type guards, Zod schemas, and explicit mappers, and only use unavoidable casts in the smallest possible scope with a comment.

🤖 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 `@apps/server/src/components/media/pro-search-builder.tsx` around lines 466 -
478, The Combobox in pro-search-builder should use a typed option model instead
of ad hoc casts, since the current optionLabel and optionTextValue logic relies
on unnecessary `as Author` and `as { name: string }`. Update the relevant
Combobox usage to `Combobox<RelationOption>` and switch the branching in the
label/value callbacks to a type guard such as `"accountId" in item` so the code
can safely distinguish author-like items from name-based items without unsafe
casts. Keep the fix localized around the existing `autocompleteItems`,
`getAuthorLabel`, and `optionLabel`/`optionTextValue` handlers.

Source: Coding guidelines

🤖 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 `@apps/server/src/application/services/backup-service.ts`:
- Around line 1746-1751: The author mapping in backup-service’s media
serialization is only using the legacy author.accountId match, so authors that
exist only in author.accounts lose their accountId/platform in the dump. Update
the simpleAuthors construction in the backup export path to treat
author.accounts as the source of truth, falling back to the single available
account when author.accountId is null or missing, and ensure the chosen
account’s accountId and platform are serialized so restore can recreate
authorAccounts correctly.
- Around line 414-419: The author resolution in backup-service currently uses
author.name as the lookup key, which can merge distinct authors that share the
same display name. Update the restore path in the loop over validItems to use
the platform + accountId composite key first, matching the platform-scoped
author ID contract, and only fall back to name-based lookup if needed. Use the
existing authorMap logic around author.platform, author.accountId, and authorId
to locate and adjust the mapping.

In `@apps/server/src/components/media/pro-search-builder.tsx`:
- Around line 466-477: The author label rendering in pro-search-builder is using
getAuthorLabel in a way that always shows accountId-backed authors as (twitter),
so update the author-specific branch in the optionLabel/optionTextValue logic to
reflect the actual platform on the Author item instead of hardcoding Twitter.
Locate the label formatting in the autocomplete options for
props.criterion.target === "author" and adjust getAuthorLabel or its caller so
Fanbox/Danbooru authors display the correct platform name consistently.

In `@apps/server/src/components/media/search-filters.tsx`:
- Line 254: The author label rendering in getAuthorLabel is hardcoding the
platform as twitter for accountId-based authors, which causes non-Twitter
authors like Fanbox to be mislabeled; update the label logic used by
search-filters.tsx so it uses author.platform instead of a fixed string, and
keep the change localized to the getAuthorLabel path or its caller where the
displayed author label is built.
- Around line 253-255: The author filter is still tracking selected values by
author.name, which can collide across platforms/accounts and break duplicate
selection or individual removal. Update the selection flow in search-filters.tsx
so the MultiSelect for authors uses the stable author.id consistently in
getSelectedItemValue and the onSelect handling, matching getItemKey; keep
display text coming from getAuthorLabel while storing and comparing by ID.

In `@packages/application/src/services/lancedb-dump-service.ts`:
- Around line 793-795: After the delta sync schema migration in
syncLanceDBDelta, update the manifest version as well so downstream readers
don’t treat the dump as an old format. Use the existing manifest write path in
lancedb-dump-service (the same area that handles migrateLanceDbSchema,
deleteMediaRows, and addRowsToTables) to persist version: LANCEDB_DUMP_VERSION
after the migration and row updates complete.

In `@packages/db/src/repositories/author-repository.ts`:
- Around line 37-42: The create() flow in author-repository.ts can return
undefined for an empty author name because the deduplication loop filters out
inputs with an empty name and then exits early when uniqueInputs is empty.
Update the Author create path so it never silently returns an empty result for
invalid input: either enforce a non-empty name at the input schema boundary or
have create() explicitly throw on empty names before reaching the
uniqueInputs/authorInputKey logic. Make the same fix in the other affected
create path referenced in the comment so Promise<Author> remains consistent.

In `@packages/ui/src/pro-search-builder.tsx`:
- Around line 466-477: The author label used by the autocomplete in
pro-search-builder’s item rendering is hardcoded to show twitter for every
author with an accountId. Update the label logic used by getAuthorLabel (and any
callers in optionLabel/optionTextValue) so it derives the displayed platform
from the actual author data instead of always defaulting to twitter, ensuring
Fanbox and Danbooru candidates render with the correct platform name.

In `@packages/ui/src/search-filters.tsx`:
- Line 233: The author label rendering is hardcoded to show “(twitter)” for
accountId-based authors, which mislabels non-Twitter authors. Update the
author-label logic used by getAuthorLabel so it reads and displays
author.platform instead of a fixed platform string, and make sure the
SearchFilters/getItemLabel path still passes through the correct author data for
display.
- Around line 232-235: The author filter is still tracking selection by
author.name, so duplicate names across platforms/accounts cannot be
distinguished. Update the author selection flow in search-filters.tsx around the
author picker props to use author.id as the selected item value and in the
onSelect handling, while keeping getItemKey aligned with the same stable ID.
Ensure the selected-state lookup, toggle/removal logic, and any state passed
through the author-related helpers all compare IDs rather than names.

---

Outside diff comments:
In `@apps/server/src/components/media/pro-search-builder.tsx`:
- Around line 481-486: The author selector is mixing ID-based options with
name-based persistence, which can restore the wrong item when duplicate names
exist. Update the selection flow in pro-search-builder.tsx so the criterion
value is stored and restored by the same ID used by optionValue, and adjust the
value lookup in the relevant select/autocomplete component to resolve by item.id
instead of item.name. Make sure the surrounding props and any setter logic tied
to this field use the same identifier consistently.

In `@packages/ui/src/pro-search-builder.tsx`:
- Around line 481-486: The author selector is mixing ID-based options with
name-based persistence, which can restore the wrong item when names collide.
Update the selection flow in pro-search-builder.tsx around the autocomplete
value handling so the saved criterion value uses the same ID returned by
optionValue, and change the value lookup to match by id rather than name. Keep
the fix consistent with the existing author selection logic in the relevant
autocomplete component and props wiring so selection and restoration both use
the same unique identifier.

---

Nitpick comments:
In `@apps/server/src/components/media/pro-search-builder.tsx`:
- Around line 466-478: The Combobox in pro-search-builder should use a typed
option model instead of ad hoc casts, since the current optionLabel and
optionTextValue logic relies on unnecessary `as Author` and `as { name: string
}`. Update the relevant Combobox usage to `Combobox<RelationOption>` and switch
the branching in the label/value callbacks to a type guard such as `"accountId"
in item` so the code can safely distinguish author-like items from name-based
items without unsafe casts. Keep the fix localized around the existing
`autocompleteItems`, `getAuthorLabel`, and `optionLabel`/`optionTextValue`
handlers.
🪄 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 Plus

Run ID: 2a21be0a-3fe8-4584-a2e2-8959c84dbd84

📥 Commits

Reviewing files that changed from the base of the PR and between 1ba477f and b01d02b.

📒 Files selected for processing (31)
  • apps/server/drizzle/0017_ambitious_gideon.sql
  • apps/server/drizzle/meta/0017_snapshot.json
  • apps/server/drizzle/meta/_journal.json
  • apps/server/src/application/services/backup-service.ts
  • apps/server/src/components/media/pro-search-builder.tsx
  • apps/server/src/components/media/search-filters.tsx
  • apps/server/src/infrastructure/jobs/download-jobs.ts
  • apps/server/src/tests/integration/backup/backup-service.test.ts
  • apps/server/src/tests/integration/repository/author-dedupe.test.ts
  • apps/server/src/tests/unit/application/services/lancedb-dump-service.test.ts
  • apps/server/src/tests/unit/application/services/media-processing-service.test.ts
  • apps/server/src/tests/unit/domain/media/schemas.test.ts
  • apps/server/src/tests/unit/infrastructure/jobs/download-jobs.test.ts
  • apps/xtracter/README.md
  • apps/xtracter/public/manifest.json
  • apps/xtracter/src/content/danbooru.ts
  • apps/xtracter/src/content/fanbox.ts
  • apps/xtracter/src/content/index.ts
  • apps/xtracter/src/content/twitter.ts
  • apps/xtracter/src/schema.ts
  • packages/application/src/services/lancedb-dump-service.ts
  • packages/application/src/services/media-processing-service.ts
  • packages/core/src/domain/authors/schemas.ts
  • packages/core/src/domain/media/schemas.ts
  • packages/core/src/domain/media/utils/filename-utils.ts
  • packages/core/src/domain/repositories/author-repository.ts
  • packages/core/tests/filename-utils.test.ts
  • packages/db/src/repositories/author-repository.ts
  • packages/db/src/schema.ts
  • packages/ui/src/pro-search-builder.tsx
  • packages/ui/src/search-filters.tsx

Comment thread apps/server/src/application/services/backup-service.ts
Comment thread apps/server/src/application/services/backup-service.ts Outdated
Comment thread apps/server/src/components/media/pro-search-builder.tsx
Comment thread apps/server/src/components/media/search-filters.tsx
Comment thread apps/server/src/components/media/search-filters.tsx
Comment thread packages/application/src/services/lancedb-dump-service.ts
Comment thread packages/db/src/repositories/author-repository.ts
Comment thread packages/ui/src/pro-search-builder.tsx
Comment thread packages/ui/src/search-filters.tsx
Comment thread packages/ui/src/search-filters.tsx
@hmjn023
hmjn023 merged commit 25097f4 into develop Jul 1, 2026
1 check passed
@hmjn023
hmjn023 deleted the feat/xtracter-fanbox-support branch July 1, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant