Skip to content

Commit

Permalink
Feat: may update transcription (#592)
Browse files Browse the repository at this point in the history
* may update transcription

* update locales & ui

* transcription post process should not break transcription
  • Loading branch information
an-lee committed May 8, 2024
1 parent 107fa97 commit 8799f5d
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 99 deletions.
5 changes: 4 additions & 1 deletion enjoy/src/i18n/en.json
Expand Up @@ -567,5 +567,8 @@
"areYouSureToDeleteThisNote": "Are you sure to delete this note?",
"notesCount": "{{count}} notes",
"source": "source",
"noNotesYet": "No notes yet"
"noNotesYet": "No notes yet",
"editTranscription": "Edit transcription",
"saveTranscription": "Save transcription",
"areYouSureToSaveTranscription": "It will perform a force-alignment between the audio and your edited transcription. Are you sure to continue?"
}
5 changes: 4 additions & 1 deletion enjoy/src/i18n/zh-CN.json
Expand Up @@ -566,5 +566,8 @@
"areYouSureToDeleteThisNote": "您确定要删除这条笔记吗?",
"notesCount": "{{count}} 条笔记",
"source": "来源",
"noNotesYet": "还没有笔记"
"noNotesYet": "还没有笔记",
"editTranscription": "编辑语音文本",
"saveTranscription": "保存语音文本",
"areYouSureToSaveTranscription": "即将根据您修改后的语音文本对语音重新进行对齐,确定要继续吗?"
}
1 change: 1 addition & 0 deletions enjoy/src/renderer/components/medias/index.ts
Expand Up @@ -5,6 +5,7 @@ export * from "./media-recordings";
export * from "./media-current-recording";
export * from "./media-recorder";
export * from "./media-transcription";
export * from "./media-transcription-form";
export * from "./media-player";
export * from "./media-provider";
export * from "./media-tabs";
Expand Down
121 changes: 121 additions & 0 deletions enjoy/src/renderer/components/medias/media-transcription-form.tsx
@@ -0,0 +1,121 @@
import { MediaPlayerProviderContext } from "@renderer/context";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
Button,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
Textarea,
toast,
} from "@renderer/components/ui";
import { TimelineEntry } from "echogarden/dist/utilities/Timeline";
import { t } from "i18next";
import { useContext, useState } from "react";
import { LoaderIcon } from "lucide-react";

export const MediaTranscriptionForm = () => {
const [open, setOpen] = useState(false);

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline" size="sm">
<span className="capitalize">{t("edit")}</span>
</Button>
</DialogTrigger>
<DialogContent className="max-w-screen-sm xl:max-w-screen-md">
<TranscriptionForm setOpen={setOpen} />
</DialogContent>
</Dialog>
);
};

export const TranscriptionForm = (props: {
setOpen: (value: boolean) => void;
}) => {
const { setOpen } = props;
const [submiting, setSubmiting] = useState(false);
const { transcription, generateTranscription } = useContext(
MediaPlayerProviderContext
);
const [content, setContent] = useState<string>(
transcription.result.timeline.map((t: TimelineEntry) => t.text).join("\n\n")
);

const handleSave = async () => {
setSubmiting(true);
try {
await generateTranscription(content);
setOpen(false);
} catch (e) {
toast.error(e.message);
}

setSubmiting(false);
};

return (
<>
<DialogHeader>
<DialogTitle>{t("editTranscription")}</DialogTitle>
</DialogHeader>
<div>
<Textarea
disabled={submiting}
className="h-96 text-lg font-serif resize-none"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
<DialogFooter>
<DialogClose asChild>
<Button disabled={submiting} variant="secondary">
{t("cancel")}
</Button>
</DialogClose>

<AlertDialog>
<AlertDialogTrigger asChild>
<Button disabled={submiting}>
{submiting && <LoaderIcon className="animate-spin w-4 mr-2" />}
{t("save")}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("saveTranscription")}</AlertDialogTitle>
<AlertDialogDescription>
{t("areYouSureToSaveTranscription")}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={submiting}>
{t("cancel")}
</AlertDialogCancel>
<AlertDialogAction asChild>
<Button disabled={submiting} onClick={handleSave}>
{submiting && (
<LoaderIcon className="animate-spin w-4 mr-2" />
)}
{t("save")}
</Button>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</DialogFooter>
</>
);
};
68 changes: 37 additions & 31 deletions enjoy/src/renderer/components/medias/media-transcription.tsx
Expand Up @@ -26,6 +26,7 @@ import {
} from "lucide-react";
import { AlignmentResult } from "echogarden/dist/api/API.d.js";
import { formatDuration } from "@renderer/lib/utils";
import { MediaTranscriptionForm } from "./media-transcription-form";

export const MediaTranscription = () => {
const containerRef = useRef<HTMLDivElement>();
Expand Down Expand Up @@ -113,37 +114,42 @@ export const MediaTranscription = () => {
)}
<span className="capitalize">{t("transcript")}</span>
</div>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
size="sm"
disabled={transcribing || transcription.state === "processing"}
className="capitalize"
>
{(transcribing || transcription.state === "processing") && (
<LoaderIcon className="animate-spin w-4 mr-2" />
)}
{transcription.result ? t("regenerate") : t("transcribe")}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("transcribe")}</AlertDialogTitle>
<AlertDialogDescription>
{t("transcribeMediaConfirmation", {
name: media.name,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction onClick={generateTranscription}>
{t("transcribe")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<div className="flex space-x-2">
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
size="sm"
disabled={
transcribing || transcription.state === "processing"
}
className="capitalize"
>
{(transcribing || transcription.state === "processing") && (
<LoaderIcon className="animate-spin w-4 mr-2" />
)}
{transcription.result ? t("regenerate") : t("transcribe")}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("transcribe")}</AlertDialogTitle>
<AlertDialogDescription>
{t("transcribeMediaConfirmation", {
name: media.name,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction onClick={() => generateTranscription("")}>
{t("transcribe")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<MediaTranscriptionForm />
</div>
</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion enjoy/src/renderer/components/ui/dialog.tsx
Expand Up @@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
Expand Down
2 changes: 1 addition & 1 deletion enjoy/src/renderer/context/media-player-provider.tsx
Expand Up @@ -66,7 +66,7 @@ type MediaPlayerContextType = {
pitchChart: Chart;
// Transcription
transcription: TranscriptionType;
generateTranscription: () => void;
generateTranscription: (text?: string) => void;
transcribing: boolean;
transcribingProgress: number;
transcriptionDraft: TranscriptionType["result"];
Expand Down

0 comments on commit 8799f5d

Please sign in to comment.