mirror of
https://github.com/Nezumi-2711/onedrive-vercel-index.git
synced 2026-09-22 13:38:45 +00:00
multiple file download with progress bar and percentage
This commit is contained in:
+17
-15
@@ -14,10 +14,13 @@ import { getExtension, getFileIcon, hasKey } from '../utils/getFileIcon'
|
|||||||
import { extensions, preview } from '../utils/getPreviewType'
|
import { extensions, preview } from '../utils/getPreviewType'
|
||||||
import { useProtectedSWRInfinite } from '../utils/fetchWithSWR'
|
import { useProtectedSWRInfinite } from '../utils/fetchWithSWR'
|
||||||
import { getBaseUrl } from '../utils/getBaseUrl'
|
import { getBaseUrl } from '../utils/getBaseUrl'
|
||||||
import { downloadMultipleFiles, downloadTreelikeMultipleFiles, traverseFolder } from '../utils/downloadMultipleFiles'
|
import {
|
||||||
|
DownloadingToast,
|
||||||
|
downloadMultipleFiles,
|
||||||
|
downloadTreelikeMultipleFiles,
|
||||||
|
traverseFolder,
|
||||||
|
} from './MultiFileDownloader'
|
||||||
|
|
||||||
import { VideoPreview } from './previews/VideoPreview'
|
|
||||||
import { AudioPreview } from './previews/AudioPreview'
|
|
||||||
import Loading, { LoadingIcon } from './Loading'
|
import Loading, { LoadingIcon } from './Loading'
|
||||||
import FourOhFour from './FourOhFour'
|
import FourOhFour from './FourOhFour'
|
||||||
import Auth from './Auth'
|
import Auth from './Auth'
|
||||||
@@ -25,6 +28,8 @@ import TextPreview from './previews/TextPreview'
|
|||||||
import MarkdownPreview from './previews/MarkdownPreview'
|
import MarkdownPreview from './previews/MarkdownPreview'
|
||||||
import CodePreview from './previews/CodePreview'
|
import CodePreview from './previews/CodePreview'
|
||||||
import OfficePreview from './previews/OfficePreview'
|
import OfficePreview from './previews/OfficePreview'
|
||||||
|
import AudioPreview from './previews/AudioPreview'
|
||||||
|
import VideoPreview from './previews/VideoPreview'
|
||||||
import DownloadBtn from './DownloadBtn'
|
import DownloadBtn from './DownloadBtn'
|
||||||
|
|
||||||
// Disabling SSR for some previews (image gallery view, and PDF view)
|
// Disabling SSR for some previews (image gallery view, and PDF view)
|
||||||
@@ -285,17 +290,16 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
|||||||
el.remove()
|
el.remove()
|
||||||
} else if (files.length > 1) {
|
} else if (files.length > 1) {
|
||||||
setTotalGenerating(true)
|
setTotalGenerating(true)
|
||||||
const toastId = toast.loading('Downloading selected files. Refresh to cancel, this may take some time...')
|
|
||||||
downloadMultipleFiles({ toastId, files, folder })
|
const toastId = toast.loading(DownloadingToast(router))
|
||||||
|
downloadMultipleFiles({ toastId, router, files, folder })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setTotalGenerating(false)
|
setTotalGenerating(false)
|
||||||
toast.dismiss(toastId)
|
toast.success('Finished downloading selected files.', { id: toastId })
|
||||||
toast.success('Finished downloading selected files.')
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setTotalGenerating(false)
|
setTotalGenerating(false)
|
||||||
toast.dismiss(toastId)
|
toast.error('Failed to download selected files.', { id: toastId })
|
||||||
toast.error('Failed to download selected files.')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,18 +318,16 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
|||||||
})()
|
})()
|
||||||
|
|
||||||
setFolderGenerating({ ...folderGenerating, [id]: true })
|
setFolderGenerating({ ...folderGenerating, [id]: true })
|
||||||
const toastId = toast.loading('Downloading folder. Refresh to cancel, this may take some time...')
|
const toastId = toast.loading(DownloadingToast(router))
|
||||||
|
|
||||||
downloadTreelikeMultipleFiles({ toastId, files, basePath: path, folder: name })
|
downloadTreelikeMultipleFiles({ toastId, router, files, basePath: path, folder: name })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setFolderGenerating({ ...folderGenerating, [id]: false })
|
setFolderGenerating({ ...folderGenerating, [id]: false })
|
||||||
toast.dismiss(toastId)
|
toast.success('Finished downloading folder.', { id: toastId })
|
||||||
toast.success('Finished downloading folder.')
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setFolderGenerating({ ...folderGenerating, [id]: false })
|
setFolderGenerating({ ...folderGenerating, [id]: false })
|
||||||
toast.dismiss(toastId)
|
toast.error('Failed to download folder.', { id: toastId })
|
||||||
toast.error('Failed to download folder.')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,37 @@
|
|||||||
|
import { NextRouter } from 'next/router'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
|
|
||||||
import { fetcher } from './fetchWithSWR'
|
import { fetcher } from '../utils/fetchWithSWR'
|
||||||
import { getStoredToken } from './protectedRouteHandler'
|
import { getStoredToken } from '../utils/protectedRouteHandler'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a loading toast with file download progress support
|
||||||
|
* @param router Next router instance, used for reloading the page
|
||||||
|
* @param progress Current downloading and compression progress (returned by jszip metadata)
|
||||||
|
* @returns Toast component with loading progress
|
||||||
|
*/
|
||||||
|
export function DownloadingToast(router: NextRouter, progress?: string) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-56">
|
||||||
|
<span>Downloading {progress ? `${progress}%` : 'selected files...'}</span>
|
||||||
|
|
||||||
|
<div className="relative mt-2">
|
||||||
|
<div className="overflow-hidden h-1 flex rounded bg-gray-100">
|
||||||
|
<div style={{ width: `${progress}%` }} className="text-white bg-gray-500 transition-all duration-100"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="p-2 rounded bg-red-500 hover:bg-red-400 text-white focus:outline-none focus:ring focus:ring-red-300"
|
||||||
|
onClick={() => router.reload()}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Blob download helper
|
// Blob download helper
|
||||||
export function downloadBlob({ blob, name }: { blob: Blob; name: string }) {
|
export function downloadBlob({ blob, name }: { blob: Blob; name: string }) {
|
||||||
@@ -28,10 +57,12 @@ export function downloadBlob({ blob, name }: { blob: Blob; name: string }) {
|
|||||||
*/
|
*/
|
||||||
export async function downloadMultipleFiles({
|
export async function downloadMultipleFiles({
|
||||||
toastId,
|
toastId,
|
||||||
|
router,
|
||||||
files,
|
files,
|
||||||
folder,
|
folder,
|
||||||
}: {
|
}: {
|
||||||
toastId: string
|
toastId: string
|
||||||
|
router: NextRouter
|
||||||
files: { name: string; url: string }[]
|
files: { name: string; url: string }[]
|
||||||
folder?: string
|
folder?: string
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
@@ -50,9 +81,7 @@ export async function downloadMultipleFiles({
|
|||||||
|
|
||||||
// Create zip file and download it
|
// Create zip file and download it
|
||||||
const b = await zip.generateAsync({ type: 'blob' }, metadata => {
|
const b = await zip.generateAsync({ type: 'blob' }, metadata => {
|
||||||
toast.loading(`Downloading ${metadata.percent.toFixed(0)}%. Refresh to cancel...`, {
|
toast.loading(DownloadingToast(router, metadata.percent.toFixed(0)), { id: toastId })
|
||||||
id: toastId,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
downloadBlob({ blob: b, name: folder ? folder + '.zip' : 'download.zip' })
|
downloadBlob({ blob: b, name: folder ? folder + '.zip' : 'download.zip' })
|
||||||
}
|
}
|
||||||
@@ -68,14 +97,15 @@ export async function downloadMultipleFiles({
|
|||||||
* @param basePath Root dir path of files to be downloaded
|
* @param basePath Root dir path of files to be downloaded
|
||||||
* @param folder Optional folder name to hold files, otherwise flatten files in the zip
|
* @param folder Optional folder name to hold files, otherwise flatten files in the zip
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function downloadTreelikeMultipleFiles({
|
export async function downloadTreelikeMultipleFiles({
|
||||||
toastId,
|
toastId,
|
||||||
|
router,
|
||||||
files,
|
files,
|
||||||
basePath,
|
basePath,
|
||||||
folder,
|
folder,
|
||||||
}: {
|
}: {
|
||||||
toastId: string
|
toastId: string
|
||||||
|
router: NextRouter
|
||||||
files: AsyncGenerator<{
|
files: AsyncGenerator<{
|
||||||
name: string
|
name: string
|
||||||
url?: string
|
url?: string
|
||||||
@@ -117,9 +147,7 @@ export async function downloadTreelikeMultipleFiles({
|
|||||||
|
|
||||||
// Create zip file and download it
|
// Create zip file and download it
|
||||||
const b = await zip.generateAsync({ type: 'blob' }, metadata => {
|
const b = await zip.generateAsync({ type: 'blob' }, metadata => {
|
||||||
toast.loading(`Downloading ${metadata.percent.toFixed(0)}%. Refresh to cancel...`, {
|
toast.loading(DownloadingToast(router, metadata.percent.toFixed(0)), { id: toastId })
|
||||||
id: toastId,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
downloadBlob({ blob: b, name: folder ? folder + '.zip' : 'download.zip' })
|
downloadBlob({ blob: b, name: folder ? folder + '.zip' : 'download.zip' })
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ enum PlayerState {
|
|||||||
Paused,
|
Paused,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
|
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -79,3 +79,5 @@ export const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default AudioPreview
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { useClipboard } from 'use-clipboard-copy'
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import toast, { Toaster } from 'react-hot-toast'
|
import toast, { Toaster } from 'react-hot-toast'
|
||||||
|
|
||||||
import { getBaseUrl } from "../../utils/getBaseUrl"
|
import { getBaseUrl } from '../../utils/getBaseUrl'
|
||||||
|
|
||||||
export const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
const { asPath } = useRouter()
|
const { asPath } = useRouter()
|
||||||
const clipboard = useClipboard()
|
const clipboard = useClipboard()
|
||||||
|
|
||||||
@@ -81,3 +81,5 @@ export const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default VideoPreview
|
||||||
|
|||||||
Reference in New Issue
Block a user