Spaces:
Running
Running
Download components/Downloader.js from Ezequield/anycoder-e2723b42: direct link, hf CLI and curl.
- Browser
- Download file 6 kB
-
https://huggingface.co/spaces/Ezequield/anycoder-e2723b42/resolve/main/components/Downloader.js
- Command line
-
hf download hf://spaces/Ezequield/anycoder-e2723b42/components/Downloader.js
-
curl -L -o Downloader.js https://huggingface.co/spaces/Ezequield/anycoder-e2723b42/resolve/main/components/Downloader.js
6 kB
| import { useState } from 'react'; | |
| import toast from 'react-hot-toast'; | |
| export default function Downloader() { | |
| const [url, setUrl] = useState(''); | |
| const [loading, setLoading] = useState(false); | |
| const [progress, setProgress] = useState(''); | |
| const [downloadLink, setDownloadLink] = useState(null); | |
| const isValidUrl = (string) => { | |
| try { | |
| new URL(string); | |
| return string.includes('deviantart.com'); | |
| } catch (_) { | |
| return false; | |
| } | |
| }; | |
| const handleProcess = async (e) => { | |
| e.preventDefault(); | |
| if (!url.trim()) { | |
| toast.error('Please enter a DeviantArt URL'); | |
| return; | |
| } | |
| if (!isValidUrl(url)) { | |
| toast.error('Please enter a valid DeviantArt URL'); | |
| return; | |
| } | |
| setLoading(true); | |
| setProgress('Initializing...'); | |
| setDownloadLink(null); | |
| try { | |
| const response = await fetch('/api/process', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ url }), | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(errorData.error || 'Failed to process request'); | |
| } | |
| // Simulate progress updates if the backend is fast | |
| setProgress('Downloading images...'); | |
| await new Promise(r => setTimeout(r, 800)); | |
| setProgress('Creating ZIP archive...'); | |
| await new Promise(r => setTimeout(r, 800)); | |
| setProgress('Uploading to MediaFire...'); | |
| await new Promise(r => setTimeout(r, 800)); | |
| const data = await response.json(); | |
| if (data.success) { | |
| setDownloadLink(data.downloadUrl); | |
| setProgress('Complete!'); | |
| toast.success('ZIP file generated successfully!'); | |
| } else { | |
| throw new Error(data.error || 'Unknown error occurred'); | |
| } | |
| } catch (error) { | |
| console.error(error); | |
| toast.error(error.message || 'Something went wrong'); | |
| setProgress('Error occurred'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="bg-gray-800/50 border border-gray-700 rounded-2xl p-6 md:p-8 shadow-xl"> | |
| <form onSubmit={handleProcess} className="space-y-6"> | |
| <div> | |
| <label htmlFor="url" className="block text-sm font-medium text-gray-300 mb-2"> | |
| DeviantArt Artwork URL | |
| </label> | |
| <div className="relative"> | |
| <input | |
| type="url" | |
| id="url" | |
| value={url} | |
| onChange={(e) => setUrl(e.target.value)} | |
| placeholder="https://www.deviantart.com/username/art/artwork-title" | |
| disabled={loading} | |
| className="w-full bg-gray-900 border border-gray-600 rounded-lg px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:border-transparent transition-all disabled:opacity-50 disabled:cursor-not-allowed" | |
| /> | |
| {url && ( | |
| <button | |
| type="button" | |
| onClick={() => setUrl('')} | |
| disabled={loading} | |
| className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-white transition-colors" | |
| > | |
| ✕ | |
| </button> | |
| )} | |
| </div> | |
| <p className="mt-2 text-xs text-gray-500"> | |
| Paste the direct link to the DeviantArt artwork page. | |
| </p> | |
| </div> | |
| <button | |
| type="submit" | |
| disabled={loading} | |
| className="w-full bg-gradient-to-r from-brand-600 to-brand-500 hover:from-brand-500 hover:to-brand-400 text-white font-semibold py-3 px-6 rounded-lg shadow-lg shadow-brand-500/20 transform hover:-translate-y-0.5 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none" | |
| > | |
| {loading ? 'Processing...' : 'Download & Zip'} | |
| </button> | |
| </form> | |
| {loading && ( | |
| <div className="mt-8 space-y-4"> | |
| <div className="flex items-center justify-between text-sm"> | |
| <span className="text-gray-400">Status</span> | |
| <span className="text-brand-400 font-medium animate-pulse">{progress}</span> | |
| </div> | |
| <div className="h-2 bg-gray-700 rounded-full overflow-hidden"> | |
| <div className="h-full bg-gradient-to-r from-brand-600 to-purple-500 w-2/3 animate-[shimmer_1.5s_infinite]"></div> | |
| </div> | |
| </div> | |
| )} | |
| {downloadLink && !loading && ( | |
| <div className="mt-8 p-4 bg-green-900/20 border border-green-700/50 rounded-lg"> | |
| <div className="flex items-start gap-3"> | |
| <div className="flex-shrink-0 w-6 h-6 rounded-full bg-green-500/20 flex items-center justify-center mt-0.5"> | |
| <svg className="w-4 h-4 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" /> | |
| </svg> | |
| </div> | |
| <div className="flex-grow"> | |
| <h4 className="text-green-400 font-medium mb-1">Ready for Download</h4> | |
| <p className="text-sm text-gray-400 mb-3"> | |
| Your image has been packaged and is ready to be downloaded. | |
| </p> | |
| <a | |
| href={downloadLink} | |
| download="deviantart-image.zip" | |
| className="inline-flex items-center gap-2 bg-green-600 hover:bg-green-500 text-white text-sm font-medium py-2 px-4 rounded transition-colors" | |
| > | |
| <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /> | |
| </svg> | |
| Download ZIP | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } |