Feat: may download files(recording/audio/video/speech) (#431)

* may download recording

* may download recording/audio/video/speech
This commit is contained in:
an-lee
2024-03-22 08:44:48 +08:00
committed by GitHub
parent 7474225b14
commit 8fc8905afc
13 changed files with 128 additions and 7 deletions

View File

@@ -227,6 +227,8 @@
"welcomeBack": "Welcome back! {{name}}",
"download": "Download",
"downloading": "Downloading {{file}}",
"downloadedSuccessfully": "Downloaded successfully",
"downloadFailed": "Download failed",
"chooseAIModelDependingOnYourHardware": "Choose AI Model depending on your hardware.",
"areYouSureToDownload": "Are you sure to download {{name}}?",
"yourModelsWillBeDownloadedTo": "Your models will be downloaded to {{path}}",

View File

@@ -227,6 +227,8 @@
"welcomeBack": "欢迎回来, {{name}}",
"download": "下载",
"downloading": "正在下载 {{file}}",
"downloadedSuccessfully": "下载成功",
"downloadFailed": "下载失败",
"chooseAIModelDependingOnYourHardware": "根据您的硬件选择合适的 AI 模型, 以便语音转文本服务正常工作",
"areYouSureToDownload": "您确定要下载 {{name}} 吗?",
"yourModelsWillBeDownloadedTo": "您的模型将下载到目录 {{path}}",

View File

@@ -134,6 +134,11 @@ export class Audio extends Model<Audio> {
return "Audio";
}
@Column(DataType.VIRTUAL)
get filename(): string {
return this.getDataValue("md5") + this.extname;
}
get extname(): string {
return (
this.getDataValue("metadata").extname ||

View File

@@ -104,6 +104,11 @@ export class Speech extends Model<Speech> {
)}`;
}
@Column(DataType.VIRTUAL)
get filename(): string {
return this.getDataValue("md5") + this.getDataValue("extname");
}
@Column(DataType.VIRTUAL)
get filePath(): string {
return path.join(

View File

@@ -134,6 +134,11 @@ export class Video extends Model<Video> {
return "Video";
}
@Column(DataType.VIRTUAL)
get filename(): string {
return this.getDataValue("md5") + this.extname;
}
get extname(): string {
return (
this.getDataValue("metadata").extname ||

View File

@@ -24,9 +24,13 @@ class Downloader {
webContents.downloadURL(url);
webContents.session.on("will-download", (_event, item, _webContents) => {
if (savePath) {
if (fs.statSync(savePath).isDirectory()) {
item.setSavePath(path.join(savePath, item.getFilename()));
} else {
try {
if (fs.statSync(savePath).isDirectory()) {
item.setSavePath(path.join(savePath, item.getFilename()));
} else {
item.setSavePath(savePath);
}
} catch {
item.setSavePath(savePath);
}
} else {
@@ -106,7 +110,7 @@ class Downloader {
registerIpcHandlers() {
ipcMain.handle("download-start", (event, url, savePath) => {
this.download(url, {
return this.download(url, {
webContents: event.sender,
savePath,
});

View File

@@ -406,7 +406,7 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
callback: (event: IpcRendererEvent, state: DownloadStateType) => void
) => ipcRenderer.on("download-on-state", callback),
start: (url: string, savePath?: string) => {
ipcRenderer.invoke("download-start", url, savePath);
return ipcRenderer.invoke("download-start", url, savePath);
},
cancel: (filename: string) => {
ipcRenderer.invoke("download-cancel", filename);

View File

@@ -39,6 +39,7 @@ import {
TextCursorInputIcon,
MicIcon,
SquareIcon,
DownloadIcon,
} from "lucide-react";
import { t } from "i18next";
import { formatDuration } from "@renderer/lib/utils";
@@ -195,6 +196,30 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
});
};
const handleDownload = () => {
EnjoyApp.dialog
.showSaveDialog({
title: t("download"),
defaultPath: currentRecording.filename,
})
.then((savePath) => {
if (!savePath) return;
toast.promise(
EnjoyApp.download.start(currentRecording.src, savePath as string),
{
loading: t("downloading", { file: currentRecording.filename }),
success: () => t("downloadedSuccessfully"),
error: t("downloadFailed"),
position: "bottom-right",
}
);
})
.catch((err) => {
toast.error(err.message);
});
};
const calContainerWidth = () => {
const w = document
.querySelector(".media-recording-container")
@@ -515,6 +540,14 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
<Share2Icon className="w-4 h-4 mr-4" />
<span>{t("share")}</span>
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer"
onClick={handleDownload}
>
<DownloadIcon className="w-4 h-4 mr-4" />
<span>{t("download")}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>

View File

@@ -29,6 +29,7 @@ import {
ZoomInIcon,
ZoomOutIcon,
MoreVerticalIcon,
DownloadIcon,
} from "lucide-react";
const ZOOM_RATIO_OPTIONS = [
@@ -93,6 +94,27 @@ export const MediaPlayer = () => {
setWidth(w - 48);
};
const handleDownload = () => {
EnjoyApp.dialog
.showSaveDialog({
title: t("download"),
defaultPath: media.filename,
})
.then((savePath) => {
if (!savePath) return;
toast.promise(EnjoyApp.download.start(media.src, savePath as string), {
loading: t("downloading", { file: media.filename }),
success: () => t("downloadedSuccessfully"),
error: t("downloadFailed"),
position: "bottom-right",
});
})
.catch((err) => {
toast.error(err.message);
});
};
useEffect(() => {
if (ref?.current) {
setRef(ref);
@@ -233,6 +255,14 @@ export const MediaPlayer = () => {
<Share2Icon className="w-4 h-4 mr-4" />
<span>{t("share")}</span>
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer"
onClick={handleDownload}
>
<DownloadIcon className="w-4 h-4 mr-4" />
<span>{t("download")}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -28,6 +28,7 @@ import {
ForwardIcon,
AlertCircleIcon,
MoreVerticalIcon,
DownloadIcon,
} from "lucide-react";
import { useCopyToClipboard } from "@uidotdev/usehooks";
import { t } from "i18next";
@@ -112,6 +113,29 @@ export const AssistantMessageComponent = (props: {
setShadowing(true);
};
const handleDownload = async () => {
if (!speech) return;
EnjoyApp.dialog
.showSaveDialog({
title: t("download"),
defaultPath: speech.filename,
})
.then((savePath) => {
if (!savePath) return;
toast.promise(EnjoyApp.download.start(speech.src, savePath as string), {
loading: t("downloading", { file: speech.filename }),
success: () => t("downloadedSuccessfully"),
error: t("downloadFailed"),
position: "bottom-right",
});
})
.catch((err) => {
toast.error(err.message);
});
};
return (
<div
id={`message-${message.id}`}
@@ -224,6 +248,15 @@ export const AssistantMessageComponent = (props: {
className="w-3 h-3 cursor-pointer"
/>
))}
{Boolean(speech) && (
<DownloadIcon
data-tooltip-id="global-tooltip"
data-tooltip-content={t("download")}
data-testid="message-start-shadow"
onClick={handleDownload}
className="w-3 h-3 cursor-pointer"
/>
)}
<DropdownMenuTrigger>
<MoreVerticalIcon className="w-3 h-3" />

View File

@@ -3,6 +3,7 @@ type AudioType = {
id: string;
source: string;
name: string;
filename: string;
description?: string;
src?: string;
coverUrl?: string;

View File

@@ -76,7 +76,7 @@ type EnjoyAppType = {
) => Promise<string[] | undefined>;
showSaveDialog: (
options: Electron.SaveDialogOptions
) => Promise<Electron.SaveDialogReturnValue>;
) => Promise<string | undefined>;
showMessageBox: (
options: Electron.MessageBoxOptions
) => Promise<Electron.MessageBoxReturnValue>;
@@ -244,7 +244,7 @@ type EnjoyAppType = {
};
download: {
onState: (callback: (event, state) => void) => void;
start: (url: string, savePath?: string) => void;
start: (url: string, savePath?: string) => Promise<string | undefined>;
cancel: (filename: string) => Promise<void>;
cancelAll: () => void;
dashboard: () => Promise<DownloadStateType[]>;

View File

@@ -3,6 +3,7 @@ type VideoType = {
id: string;
source: string;
name: string;
filename: string;
description?: string;
filename?: string;
coverUrl?: string;