Feat: download transcription from clound (#828)

* add transcription list

* download transcription from cloud

* refactor
This commit is contained in:
an-lee
2024-07-18 09:18:15 +08:00
committed by GitHub
parent 93d9e190e2
commit bb5b64cf0d
5 changed files with 238 additions and 38 deletions

View File

@@ -9,11 +9,15 @@ import {
AlertDialogFooter,
AlertDialogOverlay,
Button,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@renderer/components/ui";
import { CheckCircleIcon, LoaderIcon, XCircleIcon } from "lucide-react";
import { t } from "i18next";
import { useNavigate } from "react-router-dom";
import { TranscriptionCreateForm } from "../transcriptions";
import { TranscriptionCreateForm, TranscriptionsList } from "../transcriptions";
export const MediaLoadingModal = () => {
const navigate = useNavigate();
@@ -46,21 +50,35 @@ export const MediaLoadingModal = () => {
<span>{t("transcribedSuccessfully")}</span>
</div>
) : (
<TranscriptionCreateForm
originalText={transcription?.result?.originalText}
onSubmit={(data) => {
generateTranscription({
originalText: data.text,
language: data.language,
service: data.service as WhisperConfigType["service"],
isolate: data.isolate,
});
}}
onCancel={() => navigate(-1)}
transcribing={transcribing}
transcribingProgress={transcribingProgress}
transcribingOutput={transcribingOutput}
/>
<Tabs defaultValue="transcribe">
<TabsList className="w-full grid grid-cols-2 mb-4">
<TabsTrigger value="transcribe">{t("transcribe")}</TabsTrigger>
<TabsTrigger value="download">{t("download")}</TabsTrigger>
</TabsList>
<TabsContent value="transcribe">
<TranscriptionCreateForm
originalText={transcription?.result?.originalText}
onSubmit={(data) => {
generateTranscription({
originalText: data.text,
language: data.language,
service: data.service as WhisperConfigType["service"],
isolate: data.isolate,
});
}}
onCancel={() => navigate(-1)}
transcribing={transcribing}
transcribingProgress={transcribingProgress}
transcribingOutput={transcribingOutput}
/>
</TabsContent>
<TabsContent value="download">
<TranscriptionsList
media={media}
transcription={transcription}
/>
</TabsContent>
</Tabs>
)
) : (
<>

View File

@@ -9,10 +9,17 @@ import {
AlertDialogContent,
AlertDialogTitle,
AlertDialogDescription,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
toast,
} from "@renderer/components/ui";
import { LoaderIcon } from "lucide-react";
import { TranscriptionCreateForm } from "../transcriptions";
import {
TranscriptionCreateForm,
TranscriptionsList,
} from "@renderer/components";
export const MediaTranscriptionGenerateButton = (props: {
children: React.ReactNode;
@@ -57,27 +64,42 @@ export const MediaTranscriptionGenerateButton = (props: {
</AlertDialogDescription>
</AlertDialogHeader>
<TranscriptionCreateForm
onCancel={() => setOpen(false)}
onSubmit={(data) => {
generateTranscription({
originalText: data.text,
language: data.language,
service: data.service as WhisperConfigType["service"],
isolate: data.isolate,
})
.then(() => {
setOpen(false);
})
.catch((e) => {
toast.error(e.message);
});
}}
originalText=""
transcribing={transcribing}
transcribingProgress={transcribingProgress}
transcribingOutput={transcribingOutput}
/>
<Tabs defaultValue="transcribe">
<TabsList className="w-full grid grid-cols-2 mb-4">
<TabsTrigger value="transcribe">{t("transcribe")}</TabsTrigger>
<TabsTrigger value="download">{t("download")}</TabsTrigger>
</TabsList>
<TabsContent value="transcribe">
<TranscriptionCreateForm
onCancel={() => setOpen(false)}
onSubmit={(data) => {
generateTranscription({
originalText: data.text,
language: data.language,
service: data.service as WhisperConfigType["service"],
isolate: data.isolate,
})
.then(() => {
setOpen(false);
})
.catch((e) => {
toast.error(e.message);
});
}}
originalText=""
transcribing={transcribing}
transcribingProgress={transcribingProgress}
transcribingOutput={transcribingOutput}
/>
</TabsContent>
<TabsContent value="download">
<TranscriptionsList
media={media}
transcription={transcription}
onFinish={() => setOpen(false)}
/>
</TabsContent>
</Tabs>
</AlertDialogContent>
</AlertDialog>
);

View File

@@ -1,2 +1,3 @@
export * from "./transcription-create-form";
export * from "./transcription-edit-button";
export * from "./transcriptions-list";

View File

@@ -0,0 +1,157 @@
import {
AppSettingsProviderContext,
MediaPlayerProviderContext,
} from "@renderer/context";
import { useContext, useEffect, useState } from "react";
import { LoaderSpin } from "@renderer/components";
import {
Button,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
toast,
} from "@renderer/components/ui";
import { t } from "i18next";
import { formatDateTime } from "@renderer/lib/utils";
import {
CheckCircleIcon,
ChevronLeftIcon,
ChevronRightIcon,
DownloadCloudIcon,
} from "lucide-react";
export const TranscriptionsList = (props: {
media: AudioType | VideoType;
transcription?: TranscriptionType;
onFinish?: () => void;
}) => {
const { media, transcription, onFinish } = props;
const { webApi, EnjoyApp } = useContext(AppSettingsProviderContext);
const [transcriptions, setTranscriptions] = useState<TranscriptionType[]>([]);
const [loading, setLoading] = useState(false);
const [nextPage, setNextPage] = useState(1);
const [currentPage, setCurrentPage] = useState(1);
const fetchTranscriptions = async (params: { page: number }) => {
const { page = currentPage } = params;
setLoading(true);
webApi
.transcriptions({
targetMd5: media.md5,
items: 10,
page,
})
.then(({ transcriptions, next, page }) => {
setTranscriptions(transcriptions);
setCurrentPage(page);
setNextPage(next);
})
.finally(() => {
setLoading(false);
});
};
const handleDownload = (tr: TranscriptionType) => {
EnjoyApp.transcriptions
.update(transcription.id, {
state: "finished",
result: tr.result,
engine: tr.engine,
model: tr.model,
language: tr.language || media.language,
})
.then(() => {
onFinish?.();
})
.catch((err) => {
toast.error(err.message);
});
};
useEffect(() => {
fetchTranscriptions({ page: 1 });
}, [media]);
if (loading) {
return <LoaderSpin />;
}
if (!transcriptions.length) {
return (
<div className="text-muted-foreground text-center text-sm py-6">
{t("noData")}
</div>
);
}
return (
<div className="">
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>{t("model")}</TableHead>
<TableHead>{t("language")}</TableHead>
<TableHead>{t("date")}</TableHead>
<TableHead>{t("actions")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{transcriptions.map((tr) => (
<TableRow key={tr.id}>
<TableCell className="text-xs font-mono">
{tr.id.split("-")[0]}
</TableCell>
<TableCell>
<div className="text-sm">{tr.engine}</div>
<div className="text-xs text-muted-foreground">{tr.model}</div>
</TableCell>
<TableCell>
<span className="text-sm">{tr.language || "-"}</span>
</TableCell>
<TableCell>
<span className="text-xs">{formatDateTime(tr.createdAt)}</span>
</TableCell>
<TableCell>
{transcription?.id === tr.id ? (
<CheckCircleIcon className="text-green-600 w-4 h-4" />
) : (
<Button
variant="secondary"
size="icon"
onClick={() => handleDownload(tr)}
>
<DownloadCloudIcon className="w-4 h-4" />
</Button>
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="text-muted-foreground flex items-center justify-center space-x-2 my-4">
<Button
variant="ghost"
size="icon"
disabled={currentPage <= 1}
onClick={() => fetchTranscriptions({ page: currentPage - 1 })}
>
<ChevronLeftIcon className="w-5 h-5" />
</Button>
<span className="text-sm font-mono">{currentPage}</span>
<Button
variant="ghost"
size="icon"
disabled={!nextPage}
onClick={() => fetchTranscriptions({ page: currentPage + 1 })}
>
<ChevronRightIcon className="w-5 h-5" />
</Button>
</div>
</div>
);
};

View File

@@ -8,6 +8,8 @@ type TranscriptionType = {
model: string;
language?: string;
result: AlignmentResult & { original?: string };
createdAt: string;
updatedAt: string;
};
type TranscriptionResultSegmentType = {