mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-06-26 10:11:33 +08:00
fix: 修复 AILogo 噪音、设置页滚动与供应商批量伪内置脏数据
- AILogo: `custom` 名称为合法兜底场景,不再以 console.error 上报;其余未匹配名称降级为 console.warn - SettingPage/Model: 双栏加 `min-h-0 overflow-y-auto`,让供应商列表与右侧表单各自可滚动 - ProviderService.add_provider: API 创建一律落到 `type='custom'`,并对同名供应商抛 ValueError,避免再产生伪内置行 - CLAUDE.md: 补充 v2.0.0 子系统(RAG/Chat、可选 Nacos+RabbitMQ、i18n、cookie/transcriber 管理器) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,11 @@ interface AILogoProps {
|
||||
}
|
||||
|
||||
const AILogo = ({ name, style = 'Color', size = 24 }: AILogoProps) => {
|
||||
const Icon = Icons[name as keyof typeof Icons]
|
||||
const Icon = name ? Icons[name as keyof typeof Icons] : undefined
|
||||
if (!Icon) {
|
||||
console.error(`❌ 图标组件不存在: ${name}`)
|
||||
if (name && name !== 'custom') {
|
||||
console.warn(`AILogo: 未匹配到图标,使用自定义占位: ${name}`)
|
||||
}
|
||||
return (
|
||||
<span style={{ fontSize: size }}>
|
||||
<img src={CustomLogo} alt="CustomLogo" style={{ width: size, height: size }} />
|
||||
|
||||
@@ -7,9 +7,11 @@ interface AILogoProps {
|
||||
}
|
||||
|
||||
const AILogo = ({ name, style = 'Color', size = 24 }: AILogoProps) => {
|
||||
const Icon = Icons[name as keyof typeof Icons];
|
||||
const Icon = name ? Icons[name as keyof typeof Icons] : undefined;
|
||||
if (!Icon) {
|
||||
console.error(`❌ 图标组件不存在: ${name}`);
|
||||
if (name && name !== 'custom') {
|
||||
console.warn(`AILogo: 未匹配到图标,使用占位: ${name}`);
|
||||
}
|
||||
return <span style={{ fontSize: size }}>🚫</span>;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import { Outlet } from 'react-router-dom'
|
||||
|
||||
const Model = () => {
|
||||
return (
|
||||
<div className={'flex h-full bg-white'}>
|
||||
<div className={'flex-1/5 border-r border-neutral-200 p-2'}>
|
||||
<div className={'flex h-full min-h-0 bg-white'}>
|
||||
<div className={'flex-1/5 min-h-0 overflow-y-auto border-r border-neutral-200 p-2'}>
|
||||
<Provider></Provider>
|
||||
</div>
|
||||
<div className={'flex-4/5'}>
|
||||
<div className={'flex-4/5 min-h-0 overflow-y-auto'}>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
20
CLAUDE.md
20
CLAUDE.md
@@ -39,22 +39,31 @@ cd BillNote_frontend && pnpm tauri build
|
||||
## Architecture
|
||||
|
||||
**Backend** (`backend/`) — FastAPI app, entry point `main.py`:
|
||||
- `app/routers/` — API routes: `note.py` (generation), `provider.py`, `model.py`, `config.py`
|
||||
- `app/services/` — Business logic: `note.py` (NoteGenerator orchestrates the full pipeline), `task_serial_executor.py` (task queue)
|
||||
- `app/routers/` — API routes: `note.py` (generation), `provider.py`, `model.py`, `config.py`, `chat.py` (RAG Q&A on generated notes)
|
||||
- `app/services/` — Business logic:
|
||||
- `note.py` — `NoteGenerator` orchestrates the full pipeline (download → transcribe → LLM → notes)
|
||||
- `task_serial_executor.py` — task queue
|
||||
- `chat_service.py` + `chat_tools.py` + `vector_store.py` — RAG-based AI Q&A with Function Calling, indexing transcripts and video metadata
|
||||
- `cookie_manager.py` — per-platform cookie storage; injected into yt-dlp by downloaders (e.g. Bilibili)
|
||||
- `transcriber_config_manager.py` — persisted transcriber settings
|
||||
- `worker_registry.py` — **optional** Nacos registration + heartbeat for distributed worker mode (no-op when `NACOS_SERVER_ADDR` unset)
|
||||
- `app/messaging/` — **optional** RabbitMQ producer/consumer publishing task progress/results to `bilinote.task.feedback` exchange. Silently degrades when `RABBITMQ_URL` is unset; always import-safe.
|
||||
- `app/downloaders/` — Platform adapters (bilibili, youtube, douyin, kuaishou, local) with shared `base.py` interface
|
||||
- `app/transcriber/` — Speech-to-text engines (fast-whisper, groq, bcut, kuaishou, mlx-whisper) with factory in `transcriber_provider.py`
|
||||
- `app/transcriber/` — Speech-to-text engines (fast-whisper, groq, bcut, kuaishou, mlx-whisper) with factory in `transcriber_provider.py`. YouTube path prefers existing subtitles and skips audio download when available.
|
||||
- `app/gpt/` — LLM integration with factory pattern (`gpt_factory.py`), prompt templates (`prompt.py`, `prompt_builder.py`), and `request_chunker.py` for long transcripts
|
||||
- `app/db/` — SQLite + SQLAlchemy: DAO pattern (`provider_dao.py`, `model_dao.py`, `video_task_dao.py`), models in `models/`
|
||||
- `app/utils/` — `response.py` (ResponseWrapper for consistent JSON), `video_helper.py` (screenshots via FFmpeg), `export.py` (PDF/DOCX)
|
||||
- `app/utils/` — `response.py` (ResponseWrapper for consistent JSON), `video_helper.py` (screenshots via FFmpeg), `export.py` (PDF/DOCX), `ppt_generator.py`, `minio_client.py`
|
||||
- `app/i18n/` — backend localization
|
||||
- `events/` (root level) — Blinker signal system for post-processing (e.g., temp file cleanup after transcription)
|
||||
|
||||
**Frontend** (`BillNote_frontend/src/`) — React 19 + Vite + Tailwind + shadcn/ui:
|
||||
- `pages/HomePage/` — Main note generation UI: `NoteForm.tsx` (input), `MarkdownViewer.tsx` (preview), `MarkmapComponent.tsx` (mind map)
|
||||
- `pages/SettingPage/` — LLM provider management, system monitoring, transcriber config
|
||||
- `store/` — Zustand stores: `taskStore`, `modelStore`, `configStore`, `providerStore`
|
||||
- `store/` — Zustand stores: `taskStore`, `modelStore`, `configStore`, `providerStore`. Persists to IndexedDB.
|
||||
- `services/` — Axios API clients matching backend routes
|
||||
- `hooks/useTaskPolling.ts` — Polls task status every 3 seconds
|
||||
- `components/ui/` — shadcn/ui (Radix-based) components
|
||||
- `i18n/` — `react-i18next` setup with locale JSON in `i18n/locales/`; toggled via `components/LanguageSwitcher.tsx`
|
||||
- Path alias: `@` → `./src`
|
||||
|
||||
**Core Workflow**: User submits URL → task queued → download video → extract audio (FFmpeg) → transcribe (Whisper/Groq/etc) → generate notes (LLM) → frontend polls for completion → display Markdown + mind map.
|
||||
@@ -66,6 +75,7 @@ cd BillNote_frontend && pnpm tauri build
|
||||
- **Database**: SQLite at `backend/app/db/bili_note.db`, auto-initialized on first run
|
||||
- **FFmpeg**: Required system dependency for video/audio processing
|
||||
- **Vite proxy**: Dev server proxies `/api` and `/static` to backend (configured in `vite.config.ts`, reads env from parent dir)
|
||||
- **Distributed mode (optional)**: Setting `NACOS_SERVER_ADDR` enables Nacos worker registration; setting `RABBITMQ_URL` enables MQ feedback. Both are no-ops when unset — single-node deployment works without either. Other knobs: `WORKER_ID`, `WORKER_SELF_URL`, `WORKER_MAX_CONCURRENT`, `TASK_MAX_WORKERS`.
|
||||
|
||||
## Code Style
|
||||
|
||||
|
||||
@@ -71,11 +71,19 @@ class ProviderService:
|
||||
@staticmethod
|
||||
def add_provider( name: str, api_key: str, base_url: str, logo: str, type_: str, enabled: int = 1):
|
||||
try:
|
||||
# 内置供应商(type='built-in')只能由 seed 流程写入;API 创建一律落到 'custom',
|
||||
# 否则历史上出现过批量伪内置脏数据
|
||||
if type_ != 'custom':
|
||||
type_ = 'custom'
|
||||
existing = get_provider_by_name(name)
|
||||
if existing is not None:
|
||||
raise ValueError(f'供应商名称已存在: {name}')
|
||||
id = uuid().lower()
|
||||
logo='custom'
|
||||
logo = 'custom'
|
||||
return insert_provider(id, name, api_key, base_url, logo, type_, enabled)
|
||||
except Exception as e:
|
||||
print('创建模式失败',e)
|
||||
raise
|
||||
@staticmethod
|
||||
def provider_to_dict(p: Provider):
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user