mirror of
https://github.com/arkorty/DownLink.git
synced 2026-03-18 00:57:15 +00:00
Add a processing + progress bar
This commit is contained in:
Binary file not shown.
@@ -8,6 +8,7 @@
|
|||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"axios": "^1.7.3",
|
"axios": "^1.7.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
"react-confetti": "^6.1.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.26.0",
|
"react-router-dom": "^6.26.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
|||||||
@@ -1,29 +1,41 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import Confetti from "react-confetti";
|
||||||
|
|
||||||
const DownloadForm = () => {
|
const DownloadForm = () => {
|
||||||
const [url, setUrl] = useState("");
|
const [url, setUrl] = useState("");
|
||||||
const [quality, setQuality] = useState("720p");
|
const [quality, setQuality] = useState("720p");
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
|
const [progress, setProgress] = useState(0);
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
|
const [showConfetti, setShowConfetti] = useState(false);
|
||||||
|
const confettiRef = useRef(null);
|
||||||
|
|
||||||
const handleDownload = async (e) => {
|
const handleDownload = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setMessage("Downloading...");
|
setMessage("Processing...");
|
||||||
|
setProgress(0);
|
||||||
|
setIsProcessing(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${process.env.REACT_APP_BACKEND_URL}/downlink/download`,
|
`${process.env.REACT_APP_BACKEND_URL}/downlink/download`,
|
||||||
{ url, quality },
|
{ url, quality },
|
||||||
{
|
{
|
||||||
responseType: "blob", // Ensure the response is treated as a file
|
responseType: "blob",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
const total = progressEvent.total;
|
||||||
|
const current = progressEvent.loaded;
|
||||||
|
setProgress(Math.round((current / total) * 100));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Extract the filename from the Content-Disposition header if present
|
|
||||||
const disposition = response.headers["content-disposition"];
|
const disposition = response.headers["content-disposition"];
|
||||||
const filename = disposition
|
const filename = disposition
|
||||||
? disposition.split("filename=")[1].replace(/"/g, "")
|
? disposition.split("filename=")[1].replace(/"/g, "")
|
||||||
: "video.mp4"; // Default filename
|
: `${uuidv4()}.mp4`;
|
||||||
|
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = window.URL.createObjectURL(new Blob([response.data]));
|
link.href = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
@@ -31,14 +43,47 @@ const DownloadForm = () => {
|
|||||||
link.click();
|
link.click();
|
||||||
|
|
||||||
setMessage("Download complete");
|
setMessage("Download complete");
|
||||||
|
setIsProcessing(false);
|
||||||
|
setShowConfetti(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setMessage("Download failed");
|
setMessage("Download failed");
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getBarClass = () => {
|
||||||
|
if (message === "Download complete") return "bg-green-500";
|
||||||
|
if (message === "Download failed") return "bg-red-500";
|
||||||
|
return "bg-blue-500"; // Default color when not processing
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBarStyle = () => {
|
||||||
|
if (message === "Download complete" || message === "Download failed") {
|
||||||
|
return { transition: "width 0.5s ease-in-out" };
|
||||||
|
}
|
||||||
|
return { transition: "width 0.5s ease-in-out" };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAnimationStyle = () => {
|
||||||
|
if (message === "Download complete" || message === "Download failed") {
|
||||||
|
return { animation: "none" };
|
||||||
|
}
|
||||||
|
return { animation: "loading 1.5s infinite" };
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showConfetti) {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setShowConfetti(false);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}, [showConfetti]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4 relative">
|
||||||
<form onSubmit={handleDownload} className="space-y-4">
|
<form onSubmit={handleDownload} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@@ -77,13 +122,46 @@ const DownloadForm = () => {
|
|||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-black hover:bg-gray-700 hover:fg-black focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-black hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||||
>
|
>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{message && <p className="mt-4 text-sm text-gray-700">{message}</p>}
|
<div className="w-full bg-white rounded-full mt-4 h-3 relative overflow-hidden">
|
||||||
|
<div
|
||||||
|
className={`h-3 ${getBarClass()} rounded-full`}
|
||||||
|
style={{
|
||||||
|
width: `${progress}%`,
|
||||||
|
...getBarStyle(),
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
{isProcessing && (
|
||||||
|
<div
|
||||||
|
className={`absolute top-0 left-0 w-full h-full bg-blue-300 rounded-full`}
|
||||||
|
style={getAnimationStyle()}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{showConfetti && (
|
||||||
|
<div className="fixed top-0 left-0 w-full h-full z-50">
|
||||||
|
<Confetti
|
||||||
|
ref={confettiRef}
|
||||||
|
width={window.innerWidth}
|
||||||
|
height={window.innerHeight}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<style jsx>{`
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user