Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve audio/video adding & display #1181

Merged
merged 3 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion enjoy/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"size": "size",
"source": "source",
"createdAt": "created at",
"updatedAt": "updated at",
"recordingsCount": "recordings count",
"recordingsDuration": "recordings duration",
"isTranscribed": "transcribed",
Expand All @@ -40,6 +41,7 @@
"size": "size",
"source": "source",
"createdAt": "created at",
"updatedAt": "updated at",
"recordingsCount": "recordings count",
"recordingsDuration": "recordings duration",
"isTranscribed": "transcribed",
Expand Down Expand Up @@ -923,5 +925,7 @@
"whisperCppEngineDescription": "C++ port of the Whisper architecture.",
"ttsService": "Text to Speech Service",
"openaiTtsServiceDescription": "Use OpenAI TTS service from your own key.",
"enjoyTtsServiceDescription": "Use TTS service provided by Enjoy. OpenAI or Azure is supported."
"enjoyTtsServiceDescription": "Use TTS service provided by Enjoy. OpenAI or Azure is supported.",
"compressMediaBeforeAdding": "Compress media before adding",
"keepOriginalMedia": "Keep original media"
}
6 changes: 5 additions & 1 deletion enjoy/src/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"size": "大小",
"source": "来源",
"createdAt": "创建时间",
"updatedAt": "更新时间",
"recordingsCount": "练习次数",
"recordingsDuration": "练习时长",
"isTranscribed": "语音文本",
Expand All @@ -40,6 +41,7 @@
"size": "大小",
"source": "来源",
"createdAt": "创建时间",
"updatedAt": "更新时间",
"recordingsCount": "练习次数",
"recordingsDuration": "练习时长",
"isTranscribed": "语音文本",
Expand Down Expand Up @@ -923,5 +925,7 @@
"whisperCppEngineDescription": "Whisper 的 C++ 实现。",
"ttsService": "文字转语音服务",
"openaiTtsServiceDescription": "使用您自己的 API key 来使用 OpenAI TTS 服务。",
"enjoyTtsServiceDescription": "使用 Enjoy 提供的 TTS 服务,支持 OpenAI 或 Azure。"
"enjoyTtsServiceDescription": "使用 Enjoy 提供的 TTS 服务,支持 OpenAI 或 Azure。",
"compressMediaBeforeAdding": "添加前压缩媒体",
"keepOriginalMedia": "保存原始媒体"
}
2 changes: 2 additions & 0 deletions enjoy/src/main/db/handlers/audios-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class AudiosHandler {
name?: string;
coverUrl?: string;
originalText?: string;
compressing?: boolean;
} = {}
) {
let file = uri;
Expand All @@ -90,6 +91,7 @@ class AudiosHandler {
source,
name: params.name,
coverUrl: params.coverUrl,
compressing: params.compressing,
});

// create transcription if originalText is provided
Expand Down
1 change: 1 addition & 0 deletions enjoy/src/main/db/handlers/videos-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class VideosHandler {
name?: string;
coverUrl?: string;
md5?: string;
compressing?: boolean;
} = {}
) {
let file = uri;
Expand Down
25 changes: 19 additions & 6 deletions enjoy/src/main/db/models/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,9 @@ export class Audio extends Model<Audio> {

get extname(): string {
return (
this.getDataValue("metadata").extname ||
path.extname(this.getDataValue("source")) ||
this.getDataValue("metadata")?.extname ||
(this.getDataValue("source") &&
path.extname(this.getDataValue("source"))) ||
""
);
}
Expand Down Expand Up @@ -308,8 +309,10 @@ export class Audio extends Model<Audio> {
description?: string;
source?: string;
coverUrl?: string;
compressing?: boolean;
}
): Promise<Audio | Video> {
const { compressing = true } = params || {};
// Check if file exists
try {
fs.accessSync(filePath, fs.constants.R_OK);
Expand All @@ -334,7 +337,9 @@ export class Audio extends Model<Audio> {
},
});
if (!!existing) {
logger.warn("Audio already exists:", existing.id);
logger.warn("Audio already exists:", existing.id, existing.name);
existing.changed("updatedAt", true);
existing.update({ updatedAt: new Date() });
return existing;
}

Expand All @@ -344,7 +349,10 @@ export class Audio extends Model<Audio> {
logger.debug("Generated ID:", id);

const destDir = path.join(settings.userDataPath(), "audios");
const destFile = path.join(destDir, `${md5}.compressed.mp3`);
const destFile = path.join(
destDir,
compressing ? `${md5}.compressed.mp3` : `${md5}${extname}`
);

let metadata = {
extname,
Expand All @@ -363,8 +371,13 @@ export class Audio extends Model<Audio> {
duration: fileMetadata.format.duration,
});

// Compress file
await ffmpeg.compressAudio(filePath, destFile);
if (compressing) {
// Compress file
await ffmpeg.compressAudio(filePath, destFile);
} else {
// Copy file
fs.copyFileSync(filePath, destFile);
}

// Check if file copied
fs.accessSync(destFile, fs.constants.R_OK);
Expand Down
21 changes: 17 additions & 4 deletions enjoy/src/main/db/models/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,11 @@ export class Video extends Model<Video> {
description?: string;
source?: string;
coverUrl?: string;
compressing?: boolean;
}
): Promise<Audio | Video> {
const { compressing = true } = params || {};

// Check if file exists
try {
fs.accessSync(filePath, fs.constants.R_OK);
Expand All @@ -350,7 +353,9 @@ export class Video extends Model<Video> {
},
});
if (!!existing) {
logger.warn("Video already exists:", existing.id);
logger.warn("Video already exists:", existing.id, existing.name);
existing.changed("updatedAt", true);
existing.update({ updatedAt: new Date() });
return existing;
}

Expand All @@ -360,7 +365,10 @@ export class Video extends Model<Video> {
logger.debug("Generated ID:", id);

const destDir = path.join(settings.userDataPath(), "videos");
const destFile = path.join(destDir, `${md5}.compressed.mp4`);
const destFile = path.join(
destDir,
compressing ? `${md5}.compressed.mp4` : `${md5}${extname}`
);

let metadata = {
extname,
Expand All @@ -379,8 +387,13 @@ export class Video extends Model<Video> {
duration: fileMetadata.format.duration,
});

// Compress file to destFile
await ffmpeg.compressVideo(filePath, destFile);
if (compressing) {
// Compress file to destFile
await ffmpeg.compressVideo(filePath, destFile);
} else {
// Copy file
fs.copyFileSync(filePath, destFile);
}

// Check if file copied
fs.accessSync(destFile, fs.constants.R_OK);
Expand Down
5 changes: 5 additions & 0 deletions enjoy/src/main/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ ${log}

// Create the browser window.
const mainWindow = new BrowserWindow({
show: false,
icon:
process.platform === "win32" ? "./assets/icon.ico" : "./assets/icon.png",
width: 1280,
Expand All @@ -521,6 +522,10 @@ ${log}
},
});

mainWindow.on("ready-to-show", () => {
mainWindow.show();
});

mainWindow.on("resize", () => {
mainWindow.webContents.send("window-on-resize", mainWindow.getBounds());
});
Expand Down
55 changes: 51 additions & 4 deletions enjoy/src/renderer/components/audios/audio-card.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import { Link } from "react-router-dom";
import { cn } from "@renderer/lib/utils";
import { AudioLinesIcon, CircleAlertIcon } from "lucide-react";
import { Badge } from "@renderer/components/ui";
import {
AudioLinesIcon,
CircleAlertIcon,
EditIcon,
MoreVerticalIcon,
TrashIcon,
} from "lucide-react";
import {
Badge,
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@renderer/components/ui";
import { t } from "i18next";

export const AudioCard = (props: {
audio: Partial<AudioType>;
className?: string;
onDelete?: () => void;
onEdit?: () => void;
}) => {
const { audio, className } = props;
const { audio, className, onDelete, onEdit } = props;

return (
<div className={cn("w-full", className)}>
<div className={cn("w-full relative", className)}>
<Link to={`/audios/${audio.id}`}>
<div
className="aspect-square border rounded-lg overflow-hidden flex relative"
Expand Down Expand Up @@ -47,6 +62,38 @@ export const AudioCard = (props: {
<div className="text-sm font-semibold mt-2 max-w-full line-clamp-2 h-10">
{audio.name}
</div>
{(onDelete || onEdit) && (
<div className="absolute right-1 top-1 z-10">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="hover:bg-transparent w-6 h-6"
>
<MoreVerticalIcon className="size-4" />
</Button>
</DropdownMenuTrigger>

<DropdownMenuContent>
{onEdit && (
<DropdownMenuItem onClick={onEdit}>
<EditIcon className="size-4" />
<span className="ml-2 text-sm">{t("edit")}</span>
</DropdownMenuItem>
)}
{onDelete && (
<DropdownMenuItem onClick={onDelete}>
<TrashIcon className="size-4 text-destructive" />
<span className="ml-2 text-destructive text-sm">
{t("delete")}
</span>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
)}
</div>
);
};
20 changes: 15 additions & 5 deletions enjoy/src/renderer/components/audios/audios-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
MediaAddButton,
AudiosTable,
AudioEditForm,
LoaderSpin,
} from "@renderer/components";
import { t } from "i18next";
import {
Expand Down Expand Up @@ -255,15 +256,24 @@ export const AudiosComponent = () => {
</div>

{audios.length === 0 ? (
<div className="flex items-center justify-center h-48 border border-dashed rounded-lg">
{t("noData")}
</div>
loading ? (
<LoaderSpin />
) : (
<div className="flex items-center justify-center h-48 border border-dashed rounded-lg">
{t("noData")}
</div>
)
) : (
<>
<TabsContent value="grid">
<div className="grid gap-4 grid-cols-5">
{audios.map((audio) => (
<AudioCard audio={audio} key={audio.id} />
<AudioCard
audio={audio}
key={audio.id}
onEdit={() => setEditing(audio)}
onDelete={() => setDeleting(audio)}
/>
))}
</div>
</TabsContent>
Expand All @@ -280,7 +290,7 @@ export const AudiosComponent = () => {
</Tabs>
</div>

{hasMore && (
{!loading && hasMore && (
<div className="flex items-center justify-center my-4">
<Button variant="link" onClick={() => fetchAudios()}>
{t("loadMore")}
Expand Down
9 changes: 3 additions & 6 deletions enjoy/src/renderer/components/audios/audios-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import {
CheckCircleIcon,
CircleAlertIcon,
} from "lucide-react";
import dayjs from "@renderer/lib/dayjs";
import { secondsToTimestamp } from "@renderer/lib/utils";
import { formatDateTime, secondsToTimestamp } from "@renderer/lib/utils";
import { Link } from "react-router-dom";

export const AudiosTable = (props: {
Expand All @@ -46,7 +45,7 @@ export const AudiosTable = (props: {
{t("models.audio.recordingsDuration")}
</TableHead>
<TableHead className="capitalize">
{t("models.audio.createdAt")}
{t("models.audio.updatedAt")}
</TableHead>
<TableHead className="capitalize">
{t("models.audio.isTranscribed")}
Expand Down Expand Up @@ -92,9 +91,7 @@ export const AudiosTable = (props: {
<TableCell>
{secondsToTimestamp(audio.recordingsDuration / 1000)}
</TableCell>
<TableCell>
{dayjs(audio.createdAt).format("YYYY-MM-DD HH:mm")}
</TableCell>
<TableCell>{formatDateTime(audio.updatedAt)}</TableCell>
<TableCell>
{audio.transcribed ? (
<CheckCircleIcon className="text-green-500 w-4 h-4" />
Expand Down
Loading
Loading