mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-06-08 17:19:48 +08:00
Merge pull request #48 from JefferyHcool/dev
feat(frontend): 重构首页布局并添加生成历史组件
This commit is contained in:
@@ -2,6 +2,30 @@
|
|||||||
@import 'tw-animate-css';
|
@import 'tw-animate-css';
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
html,body{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
/* 修改滚动条轨道颜色 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px; /* 控制滚动条的宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改滚动条的轨道颜色 */
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: #f1f1f1; /* 轨道的背景颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改滚动条的滑块颜色 */
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #888; /* 滑块的颜色 */
|
||||||
|
border-radius: 4px; /* 圆角 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 当鼠标悬停时,修改滑块颜色 */
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #555; /* 悬停时的颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
@@ -21,7 +45,7 @@
|
|||||||
--accent: oklch(0.97 0 0);
|
--accent: oklch(0.97 0 0);
|
||||||
--accent-foreground: oklch(0.205 0 0);
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
--destructive: oklch(0.577 0.245 27.325);
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
--border: #e6f7ff;
|
--border: var( --color-neutral-200);
|
||||||
--input: oklch(0.922 0 0);
|
--input: oklch(0.922 0 0);
|
||||||
--ring: #096dd9;
|
--ring: #096dd9;
|
||||||
--chart-1: oklch(0.646 0.222 41.116);
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
|
|||||||
@@ -13,15 +13,16 @@ import { Link } from 'react-router-dom'
|
|||||||
interface IProps {
|
interface IProps {
|
||||||
NoteForm: React.ReactNode
|
NoteForm: React.ReactNode
|
||||||
Preview: React.ReactNode
|
Preview: React.ReactNode
|
||||||
|
History: React.ReactNode
|
||||||
}
|
}
|
||||||
const HomeLayout: FC<IProps> = ({ NoteForm, Preview }) => {
|
const HomeLayout: FC<IProps> = ({ NoteForm, Preview, History }) => {
|
||||||
const [, setShowSettings] = useState(false)
|
const [, setShowSettings] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col bg-white">
|
<div className="flex h-screen flex-col overflow-hidden bg-white">
|
||||||
<div className="flex flex-1">
|
<div className="flex flex-1">
|
||||||
{/* 左侧部分:Header + 表单 */}
|
{/* 左侧部分:Header + 表单 */}
|
||||||
<aside className="flex w-[400px] flex-col border-r border-neutral-200 bg-white">
|
<aside className="flex w-[340px] flex-col border-r border-neutral-200 bg-white">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="flex h-16 items-center justify-between px-6">
|
<header className="flex h-16 items-center justify-between px-6">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -52,6 +53,13 @@ const HomeLayout: FC<IProps> = ({ NoteForm, Preview }) => {
|
|||||||
{NoteForm}
|
{NoteForm}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
<aside className="flex h-full w-[300px] flex-col border-r border-neutral-200 bg-white">
|
||||||
|
{/* Header */}
|
||||||
|
|
||||||
|
{/* 表单内容 */}
|
||||||
|
{/*<NoteForm />*/}
|
||||||
|
{History}
|
||||||
|
</aside>
|
||||||
|
|
||||||
{/* 右侧预览区域 */}
|
{/* 右侧预览区域 */}
|
||||||
<main className="h-screen flex-1 overflow-hidden bg-white p-6">
|
<main className="h-screen flex-1 overflow-hidden bg-white p-6">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import HomeLayout from '@/layouts/HomeLayout.tsx'
|
|||||||
import NoteForm from '@/pages/HomePage/components/NoteForm.tsx'
|
import NoteForm from '@/pages/HomePage/components/NoteForm.tsx'
|
||||||
import MarkdownViewer from '@/pages/HomePage/components/MarkdownViewer.tsx'
|
import MarkdownViewer from '@/pages/HomePage/components/MarkdownViewer.tsx'
|
||||||
import { useTaskStore } from '@/store/taskStore'
|
import { useTaskStore } from '@/store/taskStore'
|
||||||
|
import History from '@/pages/HomePage/components/History.tsx'
|
||||||
type ViewStatus = 'idle' | 'loading' | 'success' | 'failed'
|
type ViewStatus = 'idle' | 'loading' | 'success' | 'failed'
|
||||||
export const HomePage: FC = () => {
|
export const HomePage: FC = () => {
|
||||||
const tasks = useTaskStore(state => state.tasks)
|
const tasks = useTaskStore(state => state.tasks)
|
||||||
@@ -36,6 +37,7 @@ export const HomePage: FC = () => {
|
|||||||
<HomeLayout
|
<HomeLayout
|
||||||
NoteForm={<NoteForm />}
|
NoteForm={<NoteForm />}
|
||||||
Preview={<MarkdownViewer status={status} content={content} />}
|
Preview={<MarkdownViewer status={status} content={content} />}
|
||||||
|
History={<History />}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
26
BillNote_frontend/src/pages/HomePage/components/History.tsx
Normal file
26
BillNote_frontend/src/pages/HomePage/components/History.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import NoteHistory from '@/pages/HomePage/components/NoteHistory.tsx'
|
||||||
|
import { useTaskStore } from '@/store/taskStore'
|
||||||
|
import { Info, Clock, Loader2 } from 'lucide-react'
|
||||||
|
import { ScrollArea } from '@/components/ui/scroll-area.tsx'
|
||||||
|
const History = () => {
|
||||||
|
const currentTaskId = useTaskStore(state => state.currentTaskId)
|
||||||
|
const setCurrentTask = useTaskStore(state => state.setCurrentTask)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={'flex h-full w-full flex-col gap-4 px-2.5 py-1.5'}>
|
||||||
|
{/*生成历史 */}
|
||||||
|
<div className="my-4 flex h-[40px] items-center gap-2">
|
||||||
|
<Clock className="h-4 w-4 text-neutral-500" />
|
||||||
|
<h2 className="text-base font-medium text-neutral-900">生成历史</h2>
|
||||||
|
</div>
|
||||||
|
<ScrollArea className="h-[800px] w-full">
|
||||||
|
{/*<div className="w-full flex-1 overflow-y-auto">*/}
|
||||||
|
<NoteHistory onSelect={setCurrentTask} selectedId={currentTaskId} />
|
||||||
|
{/*</div>*/}
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default History
|
||||||
@@ -346,7 +346,7 @@ const NoteForm = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="flex space-x-1.5">
|
<div className="flex flex-wrap space-x-1.5">
|
||||||
{noteFormats.map(item => (
|
{noteFormats.map(item => (
|
||||||
<label key={item.value} className="flex items-center space-x-2">
|
<label key={item.value} className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -383,7 +383,7 @@ const NoteForm = () => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
<Textarea placeholder={'笔记需要罗列出 xxx 关键点'} />
|
<Textarea placeholder={'笔记需要罗列出 xxx 关键点'} {...field} />
|
||||||
|
|
||||||
{/*<FormDescription className="text-xs text-neutral-500">*/}
|
{/*<FormDescription className="text-xs text-neutral-500">*/}
|
||||||
{/* 质量越高,下载体积越大,速度越慢*/}
|
{/* 质量越高,下载体积越大,速度越慢*/}
|
||||||
@@ -408,44 +408,8 @@ const NoteForm = () => {
|
|||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
{/*生成历史 */}
|
|
||||||
<div className="my-4 flex items-center gap-2">
|
|
||||||
<Clock className="h-4 w-4 text-neutral-500" />
|
|
||||||
<h2 className="text-base font-medium text-neutral-900">生成历史</h2>
|
|
||||||
</div>
|
|
||||||
<div className="min-h-0 flex-1 overflow-auto">
|
|
||||||
<NoteHistory onSelect={setCurrentTask} selectedId={currentTaskId} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 添加一些额外的说明或功能介绍 */}
|
{/* 添加一些额外的说明或功能介绍 */}
|
||||||
{showFeatureHint && (
|
|
||||||
<Alert
|
|
||||||
message="功能介绍 v2.0.0"
|
|
||||||
description={
|
|
||||||
<ul className="space-y-2 text-sm text-neutral-600">
|
|
||||||
<li className="flex items-start gap-2">
|
|
||||||
<span className="text-primary font-bold">•</span>
|
|
||||||
<span>自动提取视频内容,生成结构化笔记</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex items-start gap-2">
|
|
||||||
<span className="text-primary font-bold">•</span>
|
|
||||||
<span>支持多个视频平台,包括哔哩哔哩、YouTube等</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex items-start gap-2">
|
|
||||||
<span className="text-primary font-bold">•</span>
|
|
||||||
<span>一键复制笔记,支持Markdown格式</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex items-start gap-2">
|
|
||||||
<span className="text-primary font-bold">•</span>
|
|
||||||
<span>可选择是否插入图片</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
type="info"
|
|
||||||
onClose={onClose}
|
|
||||||
closable
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{/*<div className="bg-primary-light mt-6 rounded-lg p-4"></div>*/}
|
{/*<div className="bg-primary-light mt-6 rounded-lg p-4"></div>*/}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,76 +30,102 @@ const NoteHistory: FC<NoteHistoryProps> = ({ onSelect, selectedId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea className="h-auto max-h-[20vh] sm:max-h-[10vh]">
|
<>
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col gap-2">
|
||||||
{tasks.map(task => (
|
{tasks.map(task => (
|
||||||
<div
|
<div
|
||||||
key={task.id}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex cursor-pointer items-center gap-4 rounded-md border p-3 transition hover:bg-neutral-50',
|
'flex cursor-pointer flex-col rounded-md border border-neutral-200 p-3',
|
||||||
selectedId === task.id && 'border-primary bg-primary-light'
|
selectedId === task.id && 'border-primary bg-primary-light'
|
||||||
)}
|
)}
|
||||||
onClick={() => onSelect(task.id)}
|
|
||||||
>
|
>
|
||||||
{/* 封面图 */}
|
<div
|
||||||
<img
|
key={task.id}
|
||||||
src={
|
className={cn('flex items-center gap-4')}
|
||||||
task.audioMeta.cover_url
|
onClick={() => onSelect(task.id)}
|
||||||
? `/api/image_proxy?url=${encodeURIComponent(task.audioMeta.cover_url)}`
|
>
|
||||||
: '/placeholder.png'
|
{/* 封面图 */}
|
||||||
}
|
<img
|
||||||
alt="封面"
|
src={
|
||||||
className="h-10 w-16 rounded-md object-cover"
|
task.audioMeta.cover_url
|
||||||
/>
|
? `/api/image_proxy?url=${encodeURIComponent(task.audioMeta.cover_url)}`
|
||||||
|
: '/placeholder.png'
|
||||||
|
}
|
||||||
|
alt="封面"
|
||||||
|
className="h-10 w-12 rounded-md object-cover"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* 标题 + 状态 */}
|
{/* 标题 + 状态 */}
|
||||||
|
|
||||||
<div className="flex w-full min-w-0 items-center justify-between gap-2">
|
<div className="flex w-full items-center justify-between gap-2">
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="max-w-[120px] flex-1 truncate font-medium">
|
<div className="line-clamp-2 max-w-[180px] flex-1 overflow-hidden text-sm text-ellipsis">
|
||||||
{task.audioMeta.title || '未命名笔记'}
|
{task.audioMeta.title || '未命名笔记'}
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{task.audioMeta.title || '未命名笔记'}</p>
|
<p>{task.audioMeta.title || '未命名笔记'}</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
<div className="shrink-0">
|
|
||||||
{task.status === 'SUCCESS' && <Badge variant="default">已完成</Badge>}
|
|
||||||
{task.status === 'PENDING' && <Badge variant="outline">等待中</Badge>}
|
|
||||||
{task.status === 'FAILED' && <Badge variant="destructive">失败</Badge>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className={'mt-2 flex items-center justify-between text-[10px]'}>
|
||||||
|
<div className="shrink-0">
|
||||||
|
{task.status === 'SUCCESS' && (
|
||||||
|
<div className={'bg-primary w-10 rounded p-0.5 text-center text-white'}>
|
||||||
|
已完成
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{task.status !== 'SUCCESS' && task.status !== 'FAILED' ? (
|
||||||
|
<div className={'w-10 rounded bg-green-500 p-0.5 text-center text-white'}>
|
||||||
|
等待中
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
{task.status === 'FAILED' && (
|
||||||
|
<div className={'w-10 rounded bg-red-500 p-0.5 text-center text-white'}>失败</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 删除按钮 */}
|
<div>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="icon"
|
size="small"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
removeTask(task.id)
|
removeTask(task.id)
|
||||||
}}
|
}}
|
||||||
className="shrink-0"
|
className="shrink-0"
|
||||||
>
|
>
|
||||||
<Trash className="text-muted-foreground h-4 w-4" />
|
<Trash className="text-muted-foreground h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>删除</p>
|
<p>删除</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
{/*<div className="shrink-0">*/}
|
||||||
|
{/* {task.status === 'SUCCESS' && <Badge variant="default">已完成</Badge>}*/}
|
||||||
|
{/* {task.status !== 'SUCCESS' && task.status === 'FAILED' && (*/}
|
||||||
|
{/* <Badge variant="outline">等待中</Badge>*/}
|
||||||
|
{/* )}*/}
|
||||||
|
{/* {task.status === 'FAILED' && <Badge variant="destructive">失败</Badge>}*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,9 +41,8 @@ const StepBar: FC<StepBarProps> = ({ steps, currentStep }) => {
|
|||||||
<div className="mt-4 text-center text-xs text-gray-700">{step.label}</div>
|
<div className="mt-4 text-center text-xs text-gray-700">{step.label}</div>
|
||||||
|
|
||||||
{/* 连接线 */}
|
{/* 连接线 */}
|
||||||
{!isLast && (
|
|
||||||
<div className={`h-1 w-full ${isActive ? 'bg-primary' : 'bg-gray-300'}`}></div>
|
<div className={`h-1 w-full ${isActive ? 'bg-primary' : 'bg-gray-300'}`}></div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
28
README.md
28
README.md
@@ -3,7 +3,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./doc/icon.svg" alt="BiliNote Banner" width="50" height="50" />
|
<img src="./doc/icon.svg" alt="BiliNote Banner" width="50" height="50" />
|
||||||
</p>
|
</p>
|
||||||
<h1 align="center" > BiliNote v1.1.0</h1>
|
<h1 align="center" > BiliNote v1.1.1</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p align="center"><i>AI 视频笔记生成工具 让 AI 为你的视频做笔记</i></p>
|
<p align="center"><i>AI 视频笔记生成工具 让 AI 为你的视频做笔记</i></p>
|
||||||
@@ -46,6 +46,8 @@ BiliNote 是一个开源的 AI 视频笔记助手,支持通过哔哩哔哩、Y
|
|||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🚀 快速开始
|
||||||
|
|
||||||
@@ -133,19 +135,19 @@ DEEP_SEEK_API_KEY=xxx
|
|||||||
QWEN_API_KEY=xxx
|
QWEN_API_KEY=xxx
|
||||||
```
|
```
|
||||||
## Changelog
|
## Changelog
|
||||||
### v1.1.0
|
### v1.1.0
|
||||||
- #### Added
|
- #### Added
|
||||||
- 新增 AI 笔记风格选择
|
- 新增 AI 笔记风格选择
|
||||||
- 新增 AI 笔记返回格式选择
|
- 新增 AI 笔记返回格式选择
|
||||||
- 添加 AI 自定义笔记备注 Prompt
|
- 添加 AI 自定义笔记备注 Prompt
|
||||||
- 添加任务失败重试
|
- 添加任务失败重试
|
||||||
- 添加全局设置页,可在设置页进行模型设置
|
- 添加全局设置页,可在设置页进行模型设置
|
||||||
|
|
||||||
- #### Optimize
|
- #### Optimize
|
||||||
- 优化前端样式,优化用户体验
|
- 优化前端样式,优化用户体验
|
||||||
- 增加生成中间产物,可用于失败后加快生成速度
|
- 增加生成中间产物,可用于失败后加快生成速度
|
||||||
- #### Fix
|
- #### Fix
|
||||||
- 修复视频截图视频过早删除错误
|
- 修复视频截图视频过早删除错误
|
||||||
|
|
||||||
## 🧠 TODO
|
## 🧠 TODO
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ class YoutubeDownloader(Downloader, ABC):
|
|||||||
title = info.get("title")
|
title = info.get("title")
|
||||||
duration = info.get("duration", 0)
|
duration = info.get("duration", 0)
|
||||||
cover_url = info.get("thumbnail")
|
cover_url = info.get("thumbnail")
|
||||||
audio_path = os.path.join(output_dir, f"{video_id}.m4a")
|
ext = info.get("ext", "m4a") # 兜底用 m4a
|
||||||
|
audio_path = os.path.join(output_dir, f"{video_id}.{ext}")
|
||||||
|
print('os.path.join(output_dir, f"{video_id}.{ext}")',os.path.join(output_dir, f"{video_id}.{ext}"))
|
||||||
|
|
||||||
return AudioDownloadResult(
|
return AudioDownloadResult(
|
||||||
file_path=audio_path,
|
file_path=audio_path,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ def generate_base_prompt(title, segment_text, tags, _format=None, style=None, ex
|
|||||||
# 添加额外内容
|
# 添加额外内容
|
||||||
if extras:
|
if extras:
|
||||||
prompt += f"\n{extras}"
|
prompt += f"\n{extras}"
|
||||||
|
print(prompt)
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,10 @@ from events import transcription_finished
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
BACKEND_BASE_URL = os.getenv("API_BASE_URL", "http://localhost:8000")
|
api_path = os.getenv("API_BASE_URL", "http://localhost")
|
||||||
|
BACKEND_PORT= os.getenv("BACKEND_PORT", 8000)
|
||||||
|
|
||||||
|
BACKEND_BASE_URL = f"{api_path}:{BACKEND_PORT}"
|
||||||
output_dir = os.getenv('OUT_DIR')
|
output_dir = os.getenv('OUT_DIR')
|
||||||
image_base_url = os.getenv('IMAGE_BASE_URL')
|
image_base_url = os.getenv('IMAGE_BASE_URL')
|
||||||
logger.info("starting up")
|
logger.info("starting up")
|
||||||
@@ -129,6 +131,7 @@ class NoteGenerator:
|
|||||||
"""
|
"""
|
||||||
matches = self.extract_screenshot_timestamps(markdown)
|
matches = self.extract_screenshot_timestamps(markdown)
|
||||||
new_markdown = markdown
|
new_markdown = markdown
|
||||||
|
print(f"匹配到的截图:{matches}")
|
||||||
logger.info(f"开始为笔记生成截图")
|
logger.info(f"开始为笔记生成截图")
|
||||||
try:
|
try:
|
||||||
for idx, (marker, ts) in enumerate(matches):
|
for idx, (marker, ts) in enumerate(matches):
|
||||||
@@ -137,6 +140,7 @@ class NoteGenerator:
|
|||||||
image_url = f"{BACKEND_BASE_URL.rstrip('/')}/{image_relative_path.lstrip('/')}"
|
image_url = f"{BACKEND_BASE_URL.rstrip('/')}/{image_relative_path.lstrip('/')}"
|
||||||
replacement = f""
|
replacement = f""
|
||||||
new_markdown = new_markdown.replace(marker, replacement, 1)
|
new_markdown = new_markdown.replace(marker, replacement, 1)
|
||||||
|
print(f"替换后的 markdown:{new_markdown}")
|
||||||
|
|
||||||
return new_markdown
|
return new_markdown
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -214,7 +218,7 @@ class NoteGenerator:
|
|||||||
)
|
)
|
||||||
_path=audio.raw_info.get('path')
|
_path=audio.raw_info.get('path')
|
||||||
with open(audio_cache_path, "w", encoding="utf-8") as f:
|
with open(audio_cache_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(audio.__dict__, f, ensure_ascii=False, indent=2)
|
json.dump(asdict(audio), f, ensure_ascii=False, indent=2)
|
||||||
logger.info(f"音频下载并缓存成功,task_id={task_id}")
|
logger.info(f"音频下载并缓存成功,task_id={task_id}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 下载音频失败,task_id={task_id},错误信息:{e}")
|
logger.error(f"❌ 下载音频失败,task_id={task_id},错误信息:{e}")
|
||||||
@@ -226,13 +230,19 @@ class NoteGenerator:
|
|||||||
self.update_task_status(task_id, TaskStatus.TRANSCRIBING)
|
self.update_task_status(task_id, TaskStatus.TRANSCRIBING)
|
||||||
if os.path.exists(transcript_cache_path):
|
if os.path.exists(transcript_cache_path):
|
||||||
logger.info(f"检测到已有转写缓存,直接读取,task_id={task_id}")
|
logger.info(f"检测到已有转写缓存,直接读取,task_id={task_id}")
|
||||||
with open(transcript_cache_path, "r", encoding="utf-8") as f:
|
try:
|
||||||
transcript_data = json.load(f)
|
with open(transcript_cache_path, "r", encoding="utf-8") as f:
|
||||||
transcript = TranscriptResult(
|
transcript_data = json.load(f)
|
||||||
language=transcript_data["language"],
|
transcript = TranscriptResult(
|
||||||
full_text=transcript_data["full_text"],
|
language=transcript_data["language"],
|
||||||
segments=[TranscriptSegment(**seg) for seg in transcript_data["segments"]]
|
full_text=transcript_data["full_text"],
|
||||||
)
|
segments=[TranscriptSegment(**seg) for seg in transcript_data["segments"]]
|
||||||
|
)
|
||||||
|
except (json.JSONDecodeError, KeyError) as e:
|
||||||
|
logger.warning(f"⚠️ 读取转录缓存失败,重新转录,task_id={task_id},错误信息:{e}")
|
||||||
|
transcript: TranscriptResult = self.transcriber.transcript(file_path=audio.file_path)
|
||||||
|
with open(transcript_cache_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(asdict(transcript), f, ensure_ascii=False, indent=2)
|
||||||
else:
|
else:
|
||||||
transcript: TranscriptResult = self.transcriber.transcript(file_path=audio.file_path)
|
transcript: TranscriptResult = self.transcriber.transcript(file_path=audio.file_path)
|
||||||
with open(transcript_cache_path, "w", encoding="utf-8") as f:
|
with open(transcript_cache_path, "w", encoding="utf-8") as f:
|
||||||
|
|||||||
@@ -46,4 +46,4 @@ if __name__ == "__main__":
|
|||||||
port = int(os.getenv("BACKEND_PORT", 8000))
|
port = int(os.getenv("BACKEND_PORT", 8000))
|
||||||
host = os.getenv("BACKEND_HOST", "0.0.0.0")
|
host = os.getenv("BACKEND_HOST", "0.0.0.0")
|
||||||
logger.info(f"Starting server on {host}:{port}")
|
logger.info(f"Starting server on {host}:{port}")
|
||||||
uvicorn.run("main:app", host=host, port=port, reload=True)
|
uvicorn.run("main:app", host=host, port=port, reload=False)
|
||||||
BIN
doc/image1.png
BIN
doc/image1.png
Binary file not shown.
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 132 KiB |
BIN
doc/image2.png
BIN
doc/image2.png
Binary file not shown.
|
Before Width: | Height: | Size: 883 KiB After Width: | Height: | Size: 488 KiB |
BIN
doc/image3.png
BIN
doc/image3.png
Binary file not shown.
|
Before Width: | Height: | Size: 879 KiB After Width: | Height: | Size: 142 KiB |
Reference in New Issue
Block a user