Skip to content

Commit

Permalink
Feat: display download progress (#315)
Browse files Browse the repository at this point in the history
* cache tedtalk download url

* improve tedtalk download ux

* display progress bar

* fix locale

* update UI

* display progress when downloading audbile
  • Loading branch information
an-lee authored Feb 18, 2024
1 parent ac4d327 commit da09134
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 50 deletions.
4 changes: 3 additions & 1 deletion enjoy/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -448,5 +448,7 @@
"translationFailed": "Translation failed",
"allRecordingsSynced": "All recordings synced",
"syncingRecordings": "Syncing {{count}} recordings",
"failedToSyncRecordings": "Syncing recordings failed"
"failedToSyncRecordings": "Syncing recordings failed",
"downloadUrlNotResolved": "Download URL not resolved",
"resolvingDownloadUrl": "Resolving download URL"
}
4 changes: 3 additions & 1 deletion enjoy/src/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -447,5 +447,7 @@
"translationFailed": "翻译失败",
"allRecordingsSynced": "所有录音已同步",
"syncingRecordings": "{{count}} 条录音正在同步",
"failedToSyncRecordings": "同步录音失败"
"failedToSyncRecordings": "同步录音失败",
"downloadUrlNotResolved": "无法解析下载地址",
"resolvingDownloadUrl": "正在解析下载地址"
}
2 changes: 2 additions & 0 deletions enjoy/src/main/providers/audible-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ export class AudibleProvider {
const view = new BrowserView();
view.webContents.loadURL(this.baseURL + path);
view.webContents.on("did-finish-load", () => {
logger.debug(`Scraped ${this.baseURL + path}`);
view.webContents
.executeJavaScript(`document.documentElement.innerHTML`)
.then((html) => resolve(html as string));
});
view.webContents.on("did-fail-load", () => {
logger.error(`Failed to scrape ${this.baseURL + path}`);
reject();
});
});
Expand Down
21 changes: 21 additions & 0 deletions enjoy/src/renderer/components/audios/audible-books-segment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
DialogTitle,
DialogContent,
DialogFooter,
Progress,
} from "@renderer/components/ui";
import { t } from "i18next";
import { MediaPlayer, MediaProvider } from "@vidstack/react";
Expand All @@ -27,10 +28,12 @@ export const AudibleBooksSegment = () => {
null
);
const [downloading, setDownloading] = useState(false);
const [progress, setProgress] = useState(0);

const downloadSample = () => {
if (!selectedBook.sample) return;

setProgress(0);
setDownloading(true);
EnjoyApp.audios
.create(selectedBook.sample, {
Expand Down Expand Up @@ -73,6 +76,22 @@ export const AudibleBooksSegment = () => {
fetchAudibleBooks();
}, []);

useEffect(() => {
if (!selectedBook) return;

EnjoyApp.download.onState((_, downloadState) => {
console.log(downloadState);
const { state, received, total } = downloadState;
if (state === "progressing") {
setProgress(Math.floor((received / total) * 100));
}
});

return () => {
EnjoyApp.download.removeAllListeners();
};
}, [selectedBook]);

if (!books?.length) return null;

return (
Expand Down Expand Up @@ -158,6 +177,8 @@ export const AudibleBooksSegment = () => {
{t("downloadSample")}
</Button>
</DialogFooter>

{downloading && progress > 0 && <Progress value={progress} />}
</DialogContent>
</Dialog>
</div>
Expand Down
162 changes: 114 additions & 48 deletions enjoy/src/renderer/components/videos/ted-talks-segment.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/** @format */

import { useState, useEffect, useContext } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
import {
Expand All @@ -11,8 +9,9 @@ import {
DialogTitle,
DialogContent,
DialogFooter,
Progress,
toast,
} from "@renderer/components/ui";
import { LoaderSpin } from "@renderer/components";
import { t } from "i18next";
import { useNavigate } from "react-router-dom";
import { LoaderIcon } from "lucide-react";
Expand All @@ -33,6 +32,8 @@ export const TedTalksSegment = () => {
audio: string;
video: string;
}>();
const [resolving, setResolving] = useState(false);
const [progress, setProgress] = useState(0);

const addToLibrary = (type: DownloadType) => {
if (!downloadUrl) return;
Expand All @@ -42,13 +43,19 @@ export const TedTalksSegment = () => {
if (type === DownloadType.video) url = downloadUrl.video;
setSubmitting(true);
setSubmittingType(type);
setProgress(0);

EnjoyApp.videos
.create(url, {
name: selectedTalk?.title,
coverUrl: selectedTalk?.primaryImageSet[0].url,
})
.then((record) => {
if (!record) {
toast.error(t("failedToDownload"));
return;
}

if (type === "video") {
navigate(`/videos/${record.id}`);
} else {
Expand All @@ -61,16 +68,38 @@ export const TedTalksSegment = () => {
});
};

const downloadTalk = () => {
const resolveDowloadUrl = async () => {
if (!selectedTalk?.canonicalUrl) return;
if (resolving) return;

setResolving(true);
setDownloadUrl(null);
EnjoyApp.providers.ted
.downloadTalk(selectedTalk?.canonicalUrl)
.then((downloadUrl) => {
if (!downloadUrl) return;
setDownloadUrl(downloadUrl);
});

const cachedUrl: {
audio: string;
video: string;
} = await EnjoyApp.cacheObjects.get(
`tedtalk-download-url-${selectedTalk?.canonicalUrl}`
);
if (cachedUrl) {
setDownloadUrl(cachedUrl);
setResolving(false);
} else {
EnjoyApp.providers.ted
.downloadTalk(selectedTalk?.canonicalUrl)
.then((url) => {
if (!url) return;
EnjoyApp.cacheObjects.set(
`tedtalk-download-url-${selectedTalk?.canonicalUrl}`,
url,
60 * 60 * 24 * 7
);
setDownloadUrl(url);
})
.finally(() => {
setResolving(false);
});
}
};

const fetchTalks = async () => {
Expand Down Expand Up @@ -98,9 +127,25 @@ export const TedTalksSegment = () => {
}, []);

useEffect(() => {
downloadTalk();
resolveDowloadUrl();
}, [selectedTalk]);

useEffect(() => {
if (!downloadUrl) return;

EnjoyApp.download.onState((_, downloadState) => {
console.log(downloadState);
const { state, received, total } = downloadState;
if (state === "progressing") {
setProgress(Math.floor((received / total) * 100));
}
});

return () => {
EnjoyApp.download.removeAllListeners();
};
}, [downloadUrl]);

if (!talks?.length) return null;

return (
Expand Down Expand Up @@ -162,47 +207,68 @@ export const TedTalksSegment = () => {
</div>
</div>

{downloadUrl ? (
<DialogFooter>
<Button
variant="ghost"
onClick={() =>
EnjoyApp.shell.openExternal(selectedTalk?.canonicalUrl)
}
className="mr-auto"
>
{t("open")}
</Button>

<Button onClick={() => setSelectedTalk(null)} variant="secondary">
{t("cancel")}
</Button>
{downloadUrl.audio && (
<DialogFooter>
<Button
variant="ghost"
onClick={() =>
EnjoyApp.shell.openExternal(selectedTalk?.canonicalUrl)
}
className="mr-auto"
>
{t("open")}
</Button>

{downloadUrl ? (
<>
<Button
onClick={() => addToLibrary(DownloadType.audio)}
disabled={submitting}
onClick={() => setSelectedTalk(null)}
variant="secondary"
>
{submittingType === DownloadType.audio && (
<LoaderIcon className="w-4 h-4 animate-spin mr-2" />
)}
{t("downloadAudio")}
{t("cancel")}
</Button>
)}
{downloadUrl.video && (
<Button
onClick={() => addToLibrary(DownloadType.video)}
disabled={submitting}
{downloadUrl.audio && (
<Button
onClick={() => addToLibrary(DownloadType.audio)}
disabled={submitting}
>
{submittingType === DownloadType.audio && (
<LoaderIcon className="w-4 h-4 animate-spin mr-2" />
)}
{t("downloadAudio")}
</Button>
)}
{downloadUrl.video && (
<Button
onClick={() => addToLibrary(DownloadType.video)}
disabled={submitting}
>
{submittingType === DownloadType.video && (
<LoaderIcon className="w-4 h-4 animate-spin mr-2" />
)}
{t("downloadVideo")}
</Button>
)}
</>
) : resolving ? (
<div className="text-sm flex items-center justify-center">
<LoaderIcon className="animate-spin" />
<span className="ml-2">{t("resolvingDownloadUrl")}</span>
</div>
) : (
<div className="text-sm text-muted-foreground text-center">
{t("downloadUrlNotResolved")}
{". "}
<span
className="underline cursor-pointer"
onClick={resolveDowloadUrl}
>
{submittingType === DownloadType.video && (
<LoaderIcon className="w-4 h-4 animate-spin mr-2" />
)}
{t("downloadVideo")}
</Button>
)}
</DialogFooter>
) : (
<LoaderSpin />
)}
{t("retry")}
</span>
</div>
)}
</DialogFooter>

{submitting && progress > 0 && <Progress value={progress} />}
</DialogContent>
</Dialog>
</div>
Expand Down

0 comments on commit da09134

Please sign in to comment.