Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>PDF Magic Converter | Read, OCR & Convert Files</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .dropzone { | |
| border: 2px dashed #3b82f6; | |
| transition: all 0.3s ease; | |
| } | |
| .dropzone.active { | |
| border-color: #10b981; | |
| background-color: #f0f9ff; | |
| } | |
| .progress-bar { | |
| transition: width 0.3s ease; | |
| } | |
| .pdf-viewer { | |
| height: 70vh; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| } | |
| .format-option:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
| } | |
| .sidebar { | |
| transition: all 0.3s ease; | |
| } | |
| @media (max-width: 768px) { | |
| .sidebar { | |
| transform: translateX(-100%); | |
| } | |
| .sidebar.open { | |
| transform: translateX(0); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 font-sans"> | |
| <!-- Mobile Menu Button --> | |
| <button id="menuToggle" class="md:hidden fixed top-4 left-4 z-50 bg-blue-500 text-white p-2 rounded-lg shadow-lg"> | |
| <i class="fas fa-bars"></i> | |
| </button> | |
| <!-- Sidebar --> | |
| <div id="sidebar" class="sidebar fixed top-0 left-0 h-full w-64 bg-white shadow-lg z-40 p-4 overflow-y-auto"> | |
| <div class="flex items-center mb-8"> | |
| <i class="fas fa-file-pdf text-red-500 text-2xl mr-2"></i> | |
| <h1 class="text-xl font-bold text-gray-800">PDF Magic</h1> | |
| </div> | |
| <div class="mb-6"> | |
| <h2 class="text-sm uppercase font-semibold text-gray-500 mb-2">Navigation</h2> | |
| <ul> | |
| <li class="mb-1"> | |
| <a href="#" class="flex items-center p-2 text-blue-600 rounded-lg bg-blue-50"> | |
| <i class="fas fa-home mr-3"></i> | |
| <span>Dashboard</span> | |
| </a> | |
| </li> | |
| <li class="mb-1"> | |
| <a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg"> | |
| <i class="fas fa-history mr-3"></i> | |
| <span>Recent Files</span> | |
| </a> | |
| </li> | |
| <li class="mb-1"> | |
| <a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg"> | |
| <i class="fas fa-cog mr-3"></i> | |
| <span>Settings</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h2 class="text-sm uppercase font-semibold text-gray-500 mb-2">Tools</h2> | |
| <ul> | |
| <li class="mb-1"> | |
| <a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg"> | |
| <i class="fas fa-file-export mr-3"></i> | |
| <span>Batch Convert</span> | |
| </a> | |
| </li> | |
| <li class="mb-1"> | |
| <a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg"> | |
| <i class="fas fa-key mr-3"></i> | |
| <span>Secure PDF</span> | |
| </a> | |
| </li> | |
| <li class="mb-1"> | |
| <a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg"> | |
| <i class="fas fa-font mr-3"></i> | |
| <span>OCR Settings</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </div> | |
| <div class="mt-8 pt-4 border-t border-gray-200"> | |
| <div class="flex items-center"> | |
| <div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600"> | |
| <i class="fas fa-user"></i> | |
| </div> | |
| <div class="ml-3"> | |
| <p class="text-sm font-medium text-gray-700">John Doe</p> | |
| <p class="text-xs text-gray-500">Free Plan</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="ml-0 md:ml-64 p-4 md:p-8"> | |
| <div class="max-w-6xl mx-auto"> | |
| <header class="mb-8"> | |
| <h1 class="text-3xl font-bold text-gray-800 mb-2">PDF Converter with OCR</h1> | |
| <p class="text-gray-600">Upload your PDF files, extract text with OCR if needed, and convert to any format</p> | |
| </header> | |
| <!-- Upload Section --> | |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer"> | |
| <div class="flex flex-col items-center justify-center"> | |
| <i class="fas fa-file-upload text-4xl text-blue-500 mb-4"></i> | |
| <h3 class="text-xl font-semibold text-gray-700 mb-2">Drag & Drop your PDF file here</h3> | |
| <p class="text-gray-500 mb-4">or click to browse files</p> | |
| <input type="file" id="fileInput" class="hidden" accept=".pdf"> | |
| <button id="browseBtn" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition duration-200"> | |
| Select File | |
| </button> | |
| </div> | |
| </div> | |
| <div id="fileInfo" class="hidden mt-4 p-4 bg-blue-50 rounded-lg"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-file-pdf text-red-500 text-2xl mr-3"></i> | |
| <div class="flex-1"> | |
| <div class="flex justify-between items-center"> | |
| <span id="fileName" class="font-medium text-gray-800">sample.pdf</span> | |
| <span id="fileSize" class="text-sm text-gray-500">2.4 MB</span> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5 mt-2"> | |
| <div id="uploadProgress" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| <button id="cancelUpload" class="ml-4 text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Processing Options --> | |
| <div id="processingOptions" class="hidden bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4">Processing Options</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <!-- OCR Options --> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <div class="flex items-center mb-3"> | |
| <i class="fas fa-font text-blue-500 mr-2"></i> | |
| <h3 class="font-medium text-gray-700">OCR Settings</h3> | |
| </div> | |
| <div class="space-y-3"> | |
| <div class="flex items-center"> | |
| <input id="autoOcr" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" checked> | |
| <label for="autoOcr" class="ml-2 block text-sm text-gray-700">Auto-detect and apply OCR if needed</label> | |
| </div> | |
| <div class="ml-6 space-y-2"> | |
| <div class="flex items-center"> | |
| <input id="ocrEnglish" type="radio" name="ocrLanguage" value="eng" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" checked> | |
| <label for="ocrEnglish" class="ml-2 block text-sm text-gray-700">English</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="ocrSpanish" type="radio" name="ocrLanguage" value="spa" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"> | |
| <label for="ocrSpanish" class="ml-2 block text-sm text-gray-700">Spanish</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="ocrFrench" type="radio" name="ocrLanguage" value="fra" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"> | |
| <label for="ocrFrench" class="ml-2 block text-sm text-gray-700">French</label> | |
| </div> | |
| </div> | |
| <div class="flex items-center mt-2"> | |
| <input id="enhanceOcr" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" checked> | |
| <label for="enhanceOcr" class="ml-2 block text-sm text-gray-700">Enhance image quality before OCR</label> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Conversion Options --> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <div class="flex items-center mb-3"> | |
| <i class="fas fa-exchange-alt text-blue-500 mr-2"></i> | |
| <h3 class="font-medium text-gray-700">Conversion Options</h3> | |
| </div> | |
| <div class="space-y-3"> | |
| <label class="block text-sm font-medium text-gray-700">Output Format</label> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <button data-format="docx" class="format-option bg-white border border-gray-200 rounded-lg p-3 text-center hover:border-blue-300 transition"> | |
| <i class="fas fa-file-word text-blue-500 text-2xl mb-1"></i> | |
| <span class="block text-xs font-medium">DOCX</span> | |
| </button> | |
| <button data-format="txt" class="format-option bg-white border border-gray-200 rounded-lg p-3 text-center hover:border-blue-300 transition"> | |
| <i class="fas fa-file-alt text-gray-500 text-2xl mb-1"></i> | |
| <span class="block text-xs font-medium">TXT</span> | |
| </button> | |
| <button data-format="html" class="format-option bg-white border border-gray-200 rounded-lg p-3 text-center hover:border-blue-300 transition"> | |
| <i class="fas fa-code text-orange-500 text-2xl mb-1"></i> | |
| <span class="block text-xs font-medium">HTML</span> | |
| </button> | |
| <button data-format="epub" class="format-option bg-white border border-gray-200 rounded-lg p-3 text-center hover:border-blue-300 transition"> | |
| <i class="fas fa-book text-purple-500 text-2xl mb-1"></i> | |
| <span class="block text-xs font-medium">EPUB</span> | |
| </button> | |
| <button data-format="odt" class="format-option bg-white border border-gray-200 rounded-lg p-3 text-center hover:border-blue-300 transition"> | |
| <i class="fas fa-file-alt text-blue-400 text-2xl mb-1"></i> | |
| <span class="block text-xs font-medium">ODT</span> | |
| </button> | |
| <button data-format="markdown" class="format-option bg-white border border-gray-200 rounded-lg p-3 text-center hover:border-blue-300 transition"> | |
| <i class="fas fa-file-code text-gray-700 text-2xl mb-1"></i> | |
| <span class="block text-xs font-medium">Markdown</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6 flex justify-end"> | |
| <button id="processBtn" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition duration-200 flex items-center"> | |
| <i class="fas fa-cog mr-2"></i> Process File | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Results Section --> | |
| <div id="resultsSection" class="hidden bg-white rounded-xl shadow-md p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800">Results</h2> | |
| <div class="flex space-x-2"> | |
| <button id="downloadBtn" class="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-lg transition duration-200 flex items-center"> | |
| <i class="fas fa-download mr-2"></i> Download | |
| </button> | |
| <button id="newFileBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-medium py-2 px-4 rounded-lg transition duration-200 flex items-center"> | |
| <i class="fas fa-plus mr-2"></i> New File | |
| </button> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Original PDF Viewer --> | |
| <div class="lg:col-span-1"> | |
| <div class="bg-gray-100 rounded-lg p-3 mb-2"> | |
| <h3 class="font-medium text-gray-700 flex items-center"> | |
| <i class="fas fa-file-pdf text-red-500 mr-2"></i> Original PDF | |
| </h3> | |
| </div> | |
| <div class="pdf-viewer bg-gray-50 rounded-lg overflow-hidden"> | |
| <div class="h-full flex items-center justify-center text-gray-400"> | |
| <div class="text-center"> | |
| <i class="fas fa-file-pdf text-5xl mb-2"></i> | |
| <p>PDF Preview</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Extracted Text --> | |
| <div class="lg:col-span-2"> | |
| <div class="bg-gray-100 rounded-lg p-3 mb-2"> | |
| <h3 class="font-medium text-gray-700 flex items-center"> | |
| <i class="fas fa-font text-blue-500 mr-2"></i> Extracted Text | |
| </h3> | |
| </div> | |
| <div class="pdf-viewer bg-white border border-gray-200 rounded-lg p-4 overflow-y-auto"> | |
| <div id="extractedText" class="prose max-w-none"> | |
| <p class="text-gray-500 italic">Text will appear here after processing...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Conversion Status --> | |
| <div id="conversionStatus" class="mt-6 hidden"> | |
| <div class="bg-blue-50 border-l-4 border-blue-500 p-4"> | |
| <div class="flex"> | |
| <div class="flex-shrink-0"> | |
| <i class="fas fa-cog fa-spin text-blue-500"></i> | |
| </div> | |
| <div class="ml-3"> | |
| <h3 class="text-sm font-medium text-blue-800">Processing your file</h3> | |
| <div class="mt-2 text-sm text-blue-700"> | |
| <p id="statusMessage">Performing OCR and converting to DOCX...</p> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5 mt-2"> | |
| <div id="processProgress" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Features Section --> | |
| <div class="mt-12"> | |
| <h2 class="text-2xl font-bold text-gray-800 mb-6 text-center">Why Choose PDF Magic?</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <div class="bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition"> | |
| <div class="text-blue-500 mb-4"> | |
| <i class="fas fa-robot text-3xl"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold mb-2">Smart OCR</h3> | |
| <p class="text-gray-600">Automatically detects scanned documents and applies high-quality OCR to extract text with precision.</p> | |
| </div> | |
| <div class="bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition"> | |
| <div class="text-green-500 mb-4"> | |
| <i class="fas fa-exchange-alt text-3xl"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold mb-2">Multi-format Conversion</h3> | |
| <p class="text-gray-600">Convert your PDFs to DOCX, TXT, HTML, EPUB and more using powerful Pandoc engine.</p> | |
| </div> | |
| <div class="bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition"> | |
| <div class="text-purple-500 mb-4"> | |
| <i class="fas fa-lock text-3xl"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold mb-2">Secure Processing</h3> | |
| <p class="text-gray-600">Your files are processed securely in your browser and never uploaded to our servers.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Mobile menu toggle | |
| document.getElementById('menuToggle').addEventListener('click', function() { | |
| document.getElementById('sidebar').classList.toggle('open'); | |
| }); | |
| // File upload handling | |
| const dropzone = document.getElementById('dropzone'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const browseBtn = document.getElementById('browseBtn'); | |
| const fileInfo = document.getElementById('fileInfo'); | |
| const fileName = document.getElementById('fileName'); | |
| const fileSize = document.getElementById('fileSize'); | |
| const uploadProgress = document.getElementById('uploadProgress'); | |
| const cancelUpload = document.getElementById('cancelUpload'); | |
| const processingOptions = document.getElementById('processingOptions'); | |
| const resultsSection = document.getElementById('resultsSection'); | |
| const processBtn = document.getElementById('processBtn'); | |
| const downloadBtn = document.getElementById('downloadBtn'); | |
| const newFileBtn = document.getElementById('newFileBtn'); | |
| const conversionStatus = document.getElementById('conversionStatus'); | |
| const statusMessage = document.getElementById('statusMessage'); | |
| const processProgress = document.getElementById('processProgress'); | |
| const extractedText = document.getElementById('extractedText'); | |
| const formatOptions = document.querySelectorAll('.format-option'); | |
| let selectedFormat = 'docx'; | |
| let currentFile = null; | |
| // Highlight dropzone when dragging over | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, preventDefaults, false); | |
| }); | |
| function preventDefaults(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| } | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, highlight, false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, unhighlight, false); | |
| }); | |
| function highlight() { | |
| dropzone.classList.add('active'); | |
| } | |
| function unhighlight() { | |
| dropzone.classList.remove('active'); | |
| } | |
| // Handle dropped files | |
| dropzone.addEventListener('drop', handleDrop, false); | |
| function handleDrop(e) { | |
| const dt = e.dataTransfer; | |
| const files = dt.files; | |
| handleFiles(files); | |
| } | |
| // Handle file selection via button | |
| browseBtn.addEventListener('click', () => { | |
| fileInput.click(); | |
| }); | |
| fileInput.addEventListener('change', () => { | |
| if (fileInput.files.length) { | |
| handleFiles(fileInput.files); | |
| } | |
| }); | |
| // Cancel upload | |
| cancelUpload.addEventListener('click', () => { | |
| resetUpload(); | |
| }); | |
| // New file button | |
| newFileBtn.addEventListener('click', resetUpload); | |
| // Format selection | |
| formatOptions.forEach(option => { | |
| option.addEventListener('click', function() { | |
| formatOptions.forEach(opt => opt.classList.remove('border-blue-500', 'bg-blue-50')); | |
| this.classList.add('border-blue-500', 'bg-blue-50'); | |
| selectedFormat = this.dataset.format; | |
| }); | |
| }); | |
| // Process file button | |
| processBtn.addEventListener('click', processFile); | |
| // Download button | |
| downloadBtn.addEventListener('click', () => { | |
| alert(`Downloading file in ${selectedFormat.toUpperCase()} format`); | |
| // In a real app, this would trigger the download of the converted file | |
| }); | |
| function handleFiles(files) { | |
| currentFile = files[0]; | |
| // Update file info | |
| fileName.textContent = currentFile.name; | |
| fileSize.textContent = formatFileSize(currentFile.size); | |
| // Show file info and hide dropzone | |
| fileInfo.classList.remove('hidden'); | |
| dropzone.classList.add('hidden'); | |
| // Simulate upload progress | |
| let progress = 0; | |
| const interval = setInterval(() => { | |
| progress += Math.random() * 10; | |
| if (progress >= 100) { | |
| progress = 100; | |
| clearInterval(interval); | |
| uploadComplete(); | |
| } | |
| uploadProgress.style.width = `${progress}%`; | |
| }, 200); | |
| } | |
| function uploadComplete() { | |
| processingOptions.classList.remove('hidden'); | |
| } | |
| function processFile() { | |
| processingOptions.classList.add('hidden'); | |
| conversionStatus.classList.remove('hidden'); | |
| resultsSection.classList.remove('hidden'); | |
| // Simulate processing steps | |
| let progress = 0; | |
| const steps = [ | |
| {progress: 20, message: "Analyzing PDF content..."}, | |
| {progress: 40, message: "Detecting text layers..."}, | |
| {progress: 60, message: "Applying OCR to scanned pages..."}, | |
| {progress: 80, message: `Converting to ${selectedFormat.toUpperCase()} format...`}, | |
| {progress: 100, message: "Conversion complete!"} | |
| ]; | |
| let currentStep = 0; | |
| const processInterval = setInterval(() => { | |
| if (currentStep < steps.length) { | |
| progress = steps[currentStep].progress; | |
| statusMessage.textContent = steps[currentStep].message; | |
| processProgress.style.width = `${progress}%`; | |
| currentStep++; | |
| // Update extracted text at certain steps | |
| if (currentStep === 3) { | |
| extractedText.innerHTML = ` | |
| <h1>Sample Extracted Document</h1> | |
| <p>This is text that was extracted from your PDF document using our advanced OCR technology.</p> | |
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in dui mauris. Vivamus hendrerit arcu sed erat molestie vehicula.</p> | |
| <h2>Section 1</h2> | |
| <p>Sed auctor neque eu tellus rhoncus ut eleifend nibh porttitor. Ut in nulla enim. Phasellus molestie magna non est bibendum non venenatis nisl tempor.</p> | |
| <p>This text would normally be extracted from your actual PDF file.</p> | |
| `; | |
| } | |
| } else { | |
| clearInterval(processInterval); | |
| conversionStatus.classList.add('hidden'); | |
| } | |
| }, 1500); | |
| } | |
| function resetUpload() { | |
| fileInfo.classList.add('hidden'); | |
| processingOptions.classList.add('hidden'); | |
| resultsSection.classList.add('hidden'); | |
| conversionStatus.classList.add('hidden'); | |
| dropzone.classList.remove('hidden'); | |
| uploadProgress.style.width = '0%'; | |
| processProgress.style.width = '0%'; | |
| fileInput.value = ''; | |
| currentFile = null; | |
| extractedText.innerHTML = '<p class="text-gray-500 italic">Text will appear here after processing...</p>'; | |
| } | |
| function formatFileSize(bytes) { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| } | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=RedSparkie/epub" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |