Spaces:
Sleeping
Sleeping
zaysrwk commited on
Commit ·
9b703ea
1
Parent(s): 6df4679
Add progress indicators and improve image enhancement functionality
Browse filesImplement asynchronous processing with progress polling for image enhancement and other features, including UI updates for progress display and improved error handling for API responses.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 064f791d-a5ee-4eab-ab49-cbb215105dc0
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: f6a563d0-2106-4108-9f7d-27dee4df41da
- my_ssh_key.txt +0 -0
- templates/index.html +125 -6
my_ssh_key.txt
DELETED
|
File without changes
|
templates/index.html
CHANGED
|
@@ -264,6 +264,40 @@
|
|
| 264 |
to { transform: rotate(360deg); }
|
| 265 |
}
|
| 266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
.error {
|
| 268 |
background: rgba(255, 50, 50, 0.2);
|
| 269 |
border: 1px solid #ff3232;
|
|
@@ -425,6 +459,13 @@
|
|
| 425 |
<div class="loading" id="loading">
|
| 426 |
<div class="spinner"></div>
|
| 427 |
<p id="loadingText">Processing your image with AI...</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 428 |
<p><small>This may take a moment</small></p>
|
| 429 |
</div>
|
| 430 |
|
|
@@ -482,6 +523,9 @@
|
|
| 482 |
const downloadBtn = document.getElementById('downloadBtn');
|
| 483 |
const resultBox = document.getElementById('resultBox');
|
| 484 |
const resultLabel = document.getElementById('resultLabel');
|
|
|
|
|
|
|
|
|
|
| 485 |
|
| 486 |
const featureTabs = document.querySelectorAll('.feature-tab');
|
| 487 |
const bgcolorSelect = document.getElementById('bgcolor');
|
|
@@ -575,22 +619,91 @@
|
|
| 575 |
hideError();
|
| 576 |
}
|
| 577 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
processBtn.addEventListener('click', async () => {
|
| 579 |
if (!selectedFile) return;
|
| 580 |
|
| 581 |
const formData = new FormData();
|
| 582 |
formData.append('file', selectedFile);
|
| 583 |
|
| 584 |
-
let endpoint = '/enhance';
|
| 585 |
let params = new URLSearchParams();
|
| 586 |
|
| 587 |
if (currentFeature === 'enhance') {
|
|
|
|
| 588 |
const scale = document.getElementById('scale').value;
|
| 589 |
params.append('scale', scale);
|
| 590 |
loadingText.textContent = 'Enhancing your image with AI...';
|
| 591 |
resultLabel.textContent = 'Enhanced';
|
| 592 |
} else if (currentFeature === 'remove-bg') {
|
| 593 |
-
endpoint = '/remove-background';
|
| 594 |
let bgcolor = bgcolorSelect.value;
|
| 595 |
if (bgcolor === 'custom') {
|
| 596 |
bgcolor = document.getElementById('customColor').value;
|
|
@@ -600,13 +713,13 @@
|
|
| 600 |
resultLabel.textContent = 'Background Removed';
|
| 601 |
resultBox.classList.add('checkerboard');
|
| 602 |
} else if (currentFeature === 'denoise') {
|
| 603 |
-
endpoint = '/denoise';
|
| 604 |
const strength = document.getElementById('strength').value;
|
| 605 |
params.append('strength', strength);
|
| 606 |
loadingText.textContent = 'Reducing noise in your image...';
|
| 607 |
resultLabel.textContent = 'Denoised';
|
| 608 |
} else if (currentFeature === 'docscan') {
|
| 609 |
-
endpoint = '/docscan';
|
| 610 |
const docScale = document.getElementById('docScale').value;
|
| 611 |
const enhanceHd = document.getElementById('enhanceHd').value;
|
| 612 |
params.append('scale', docScale);
|
|
@@ -623,6 +736,7 @@
|
|
| 623 |
results.classList.remove('show');
|
| 624 |
processBtn.disabled = true;
|
| 625 |
hideError();
|
|
|
|
| 626 |
|
| 627 |
try {
|
| 628 |
const response = await fetch(`${endpoint}?${params.toString()}`, {
|
|
@@ -635,8 +749,13 @@
|
|
| 635 |
throw new Error(errorData.detail || 'Processing failed');
|
| 636 |
}
|
| 637 |
|
| 638 |
-
const
|
| 639 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 640 |
|
| 641 |
processedImg.src = imageUrl;
|
| 642 |
downloadBtn.href = imageUrl;
|
|
|
|
| 264 |
to { transform: rotate(360deg); }
|
| 265 |
}
|
| 266 |
|
| 267 |
+
.progress-container {
|
| 268 |
+
width: 100%;
|
| 269 |
+
max-width: 400px;
|
| 270 |
+
margin: 20px auto;
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
.progress-bar-wrapper {
|
| 274 |
+
background: rgba(255, 255, 255, 0.1);
|
| 275 |
+
border-radius: 10px;
|
| 276 |
+
overflow: hidden;
|
| 277 |
+
height: 20px;
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
.progress-bar {
|
| 281 |
+
height: 100%;
|
| 282 |
+
background: linear-gradient(90deg, #00d2ff, #3a7bd5);
|
| 283 |
+
border-radius: 10px;
|
| 284 |
+
transition: width 0.3s ease;
|
| 285 |
+
width: 0%;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
.progress-text {
|
| 289 |
+
margin-top: 10px;
|
| 290 |
+
color: #888;
|
| 291 |
+
font-size: 0.9rem;
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
.progress-percentage {
|
| 295 |
+
color: #00d2ff;
|
| 296 |
+
font-weight: 600;
|
| 297 |
+
font-size: 1.1rem;
|
| 298 |
+
margin-bottom: 5px;
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
.error {
|
| 302 |
background: rgba(255, 50, 50, 0.2);
|
| 303 |
border: 1px solid #ff3232;
|
|
|
|
| 459 |
<div class="loading" id="loading">
|
| 460 |
<div class="spinner"></div>
|
| 461 |
<p id="loadingText">Processing your image with AI...</p>
|
| 462 |
+
<div class="progress-container">
|
| 463 |
+
<div class="progress-percentage" id="progressPercentage">0%</div>
|
| 464 |
+
<div class="progress-bar-wrapper">
|
| 465 |
+
<div class="progress-bar" id="progressBar"></div>
|
| 466 |
+
</div>
|
| 467 |
+
<p class="progress-text" id="progressMessage">Initializing...</p>
|
| 468 |
+
</div>
|
| 469 |
<p><small>This may take a moment</small></p>
|
| 470 |
</div>
|
| 471 |
|
|
|
|
| 523 |
const downloadBtn = document.getElementById('downloadBtn');
|
| 524 |
const resultBox = document.getElementById('resultBox');
|
| 525 |
const resultLabel = document.getElementById('resultLabel');
|
| 526 |
+
const progressBar = document.getElementById('progressBar');
|
| 527 |
+
const progressPercentage = document.getElementById('progressPercentage');
|
| 528 |
+
const progressMessage = document.getElementById('progressMessage');
|
| 529 |
|
| 530 |
const featureTabs = document.querySelectorAll('.feature-tab');
|
| 531 |
const bgcolorSelect = document.getElementById('bgcolor');
|
|
|
|
| 619 |
hideError();
|
| 620 |
}
|
| 621 |
|
| 622 |
+
function updateProgress(progress, message) {
|
| 623 |
+
progressBar.style.width = `${progress}%`;
|
| 624 |
+
progressPercentage.textContent = `${Math.round(progress)}%`;
|
| 625 |
+
if (message) {
|
| 626 |
+
progressMessage.textContent = message;
|
| 627 |
+
}
|
| 628 |
+
}
|
| 629 |
+
|
| 630 |
+
function resetProgress() {
|
| 631 |
+
progressBar.style.width = '0%';
|
| 632 |
+
progressPercentage.textContent = '0%';
|
| 633 |
+
progressMessage.textContent = 'Initializing...';
|
| 634 |
+
}
|
| 635 |
+
|
| 636 |
+
async function pollProgress(jobId, resultUrl) {
|
| 637 |
+
const progressUrl = `/progress/${jobId}`;
|
| 638 |
+
const maxRetries = 600;
|
| 639 |
+
let retries = 0;
|
| 640 |
+
|
| 641 |
+
while (retries < maxRetries) {
|
| 642 |
+
retries++;
|
| 643 |
+
|
| 644 |
+
try {
|
| 645 |
+
const response = await fetch(progressUrl);
|
| 646 |
+
if (!response.ok) {
|
| 647 |
+
throw new Error('Failed to get progress');
|
| 648 |
+
}
|
| 649 |
+
|
| 650 |
+
const data = await response.json();
|
| 651 |
+
updateProgress(data.progress, data.message);
|
| 652 |
+
|
| 653 |
+
if (data.status === 'completed') {
|
| 654 |
+
let resultRetries = 0;
|
| 655 |
+
while (resultRetries < 10) {
|
| 656 |
+
const resultResponse = await fetch(resultUrl);
|
| 657 |
+
|
| 658 |
+
if (resultResponse.status === 202) {
|
| 659 |
+
resultRetries++;
|
| 660 |
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
| 661 |
+
continue;
|
| 662 |
+
}
|
| 663 |
+
|
| 664 |
+
if (!resultResponse.ok) {
|
| 665 |
+
let errorMessage = 'Failed to get result';
|
| 666 |
+
try {
|
| 667 |
+
const errorData = await resultResponse.json();
|
| 668 |
+
errorMessage = errorData.detail || errorMessage;
|
| 669 |
+
} catch (e) {}
|
| 670 |
+
throw new Error(errorMessage);
|
| 671 |
+
}
|
| 672 |
+
|
| 673 |
+
const blob = await resultResponse.blob();
|
| 674 |
+
return URL.createObjectURL(blob);
|
| 675 |
+
}
|
| 676 |
+
throw new Error('Timed out waiting for result');
|
| 677 |
+
} else if (data.status === 'failed') {
|
| 678 |
+
throw new Error(data.error || data.message || 'Processing failed');
|
| 679 |
+
}
|
| 680 |
+
|
| 681 |
+
await new Promise(resolve => setTimeout(resolve, 500));
|
| 682 |
+
} catch (err) {
|
| 683 |
+
throw err;
|
| 684 |
+
}
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
+
throw new Error('Timed out waiting for processing to complete');
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
processBtn.addEventListener('click', async () => {
|
| 691 |
if (!selectedFile) return;
|
| 692 |
|
| 693 |
const formData = new FormData();
|
| 694 |
formData.append('file', selectedFile);
|
| 695 |
|
| 696 |
+
let endpoint = '/enhance/async';
|
| 697 |
let params = new URLSearchParams();
|
| 698 |
|
| 699 |
if (currentFeature === 'enhance') {
|
| 700 |
+
endpoint = '/enhance/async';
|
| 701 |
const scale = document.getElementById('scale').value;
|
| 702 |
params.append('scale', scale);
|
| 703 |
loadingText.textContent = 'Enhancing your image with AI...';
|
| 704 |
resultLabel.textContent = 'Enhanced';
|
| 705 |
} else if (currentFeature === 'remove-bg') {
|
| 706 |
+
endpoint = '/remove-background/async';
|
| 707 |
let bgcolor = bgcolorSelect.value;
|
| 708 |
if (bgcolor === 'custom') {
|
| 709 |
bgcolor = document.getElementById('customColor').value;
|
|
|
|
| 713 |
resultLabel.textContent = 'Background Removed';
|
| 714 |
resultBox.classList.add('checkerboard');
|
| 715 |
} else if (currentFeature === 'denoise') {
|
| 716 |
+
endpoint = '/denoise/async';
|
| 717 |
const strength = document.getElementById('strength').value;
|
| 718 |
params.append('strength', strength);
|
| 719 |
loadingText.textContent = 'Reducing noise in your image...';
|
| 720 |
resultLabel.textContent = 'Denoised';
|
| 721 |
} else if (currentFeature === 'docscan') {
|
| 722 |
+
endpoint = '/docscan/async';
|
| 723 |
const docScale = document.getElementById('docScale').value;
|
| 724 |
const enhanceHd = document.getElementById('enhanceHd').value;
|
| 725 |
params.append('scale', docScale);
|
|
|
|
| 736 |
results.classList.remove('show');
|
| 737 |
processBtn.disabled = true;
|
| 738 |
hideError();
|
| 739 |
+
resetProgress();
|
| 740 |
|
| 741 |
try {
|
| 742 |
const response = await fetch(`${endpoint}?${params.toString()}`, {
|
|
|
|
| 749 |
throw new Error(errorData.detail || 'Processing failed');
|
| 750 |
}
|
| 751 |
|
| 752 |
+
const jobData = await response.json();
|
| 753 |
+
const jobId = jobData.job_id;
|
| 754 |
+
const resultUrl = jobData.result_url;
|
| 755 |
+
|
| 756 |
+
updateProgress(5, 'Job started...');
|
| 757 |
+
|
| 758 |
+
const imageUrl = await pollProgress(jobId, resultUrl);
|
| 759 |
|
| 760 |
processedImg.src = imageUrl;
|
| 761 |
downloadBtn.href = imageUrl;
|