show progress
This commit is contained in:
+222
-31
@@ -41,12 +41,12 @@
|
||||
disabled="@isProcessing" />
|
||||
<button @onclick="DownloadMp3"
|
||||
@onmousedown="PreventDefault"
|
||||
class="download-btn"
|
||||
class="download-btn @(isProcessing ? "processing" : "")"
|
||||
disabled="@(isProcessing)">
|
||||
<span class="btn-icon">🎵</span>
|
||||
@if (isProcessing)
|
||||
{
|
||||
<span>Processing...</span>
|
||||
<span>Processing... (@progressPercentage%)</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -55,6 +55,65 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@* Enhanced Progress Section - Show immediately when processing starts *@
|
||||
@if (isProcessing)
|
||||
{
|
||||
<div class="enhanced-progress-section">
|
||||
<div class="progress-header">
|
||||
<h3 class="progress-title">
|
||||
<span class="progress-icon @GetProgressIconClass()">@GetProgressIcon()</span>
|
||||
@GetProgressStageTitle()
|
||||
</h3>
|
||||
<div class="progress-stats">
|
||||
<span class="progress-percentage">@progressPercentage%</span>
|
||||
<span class="progress-elapsed">⏱️ @GetElapsedTime()</span>
|
||||
@if (estimatedTimeRemaining.HasValue && estimatedTimeRemaining.Value > TimeSpan.Zero)
|
||||
{
|
||||
<span class="progress-eta">ETA: @GetEstimatedTimeRemaining()</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress-bar-background">
|
||||
<div class="progress-bar-fill" style="width: @(progressPercentage)%">
|
||||
<div class="progress-bar-shimmer"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-stages">
|
||||
@foreach (var stage in progressStages)
|
||||
{
|
||||
<div class="progress-stage @GetStageClass(stage)">
|
||||
<div class="stage-icon">@stage.Icon</div>
|
||||
<div class="stage-label">@stage.Label</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-details">
|
||||
<div class="progress-message">
|
||||
<span class="message-text">@progressMessage</span>
|
||||
@if (progressPercentage > 0 && progressPercentage < 100)
|
||||
{
|
||||
<div class="progress-dots">
|
||||
<span class="dot">.</span>
|
||||
<span class="dot">.</span>
|
||||
<span class="dot">.</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (currentStageDetails != null)
|
||||
{
|
||||
<div class="stage-details">
|
||||
<small>@currentStageDetails</small>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@* Video Information Section - Show as soon as we have details *@
|
||||
@if (currentVideoDetails != null)
|
||||
{
|
||||
@@ -69,6 +128,7 @@
|
||||
{
|
||||
<div class="processing-overlay">
|
||||
<div class="processing-spinner">🎵</div>
|
||||
<div class="processing-text">@progressPercentage%</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -80,29 +140,10 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (isProcessing)
|
||||
{
|
||||
<div class="progress-container @(currentVideoDetails != null ? "with-video-details" : "")">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: @(progressPercentage)%"></div>
|
||||
</div>
|
||||
<div class="progress-text">
|
||||
@if (currentVideoDetails != null && progressPercentage > 15)
|
||||
{
|
||||
<span class="progress-highlight">@progressMessage</span>
|
||||
<span class="progress-percentage">(@progressPercentage%)</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@progressMessage (@progressPercentage%)</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(outputMessage) && !isProcessing)
|
||||
{
|
||||
<div class="output-message">
|
||||
<div class="output-message success">
|
||||
<div class="success-icon">✅</div>
|
||||
<p>@outputMessage</p>
|
||||
</div>
|
||||
}
|
||||
@@ -111,7 +152,8 @@
|
||||
@if (!string.IsNullOrWhiteSpace(apiErrorMessage) && !isProcessing)
|
||||
{
|
||||
<div class="error-message">
|
||||
<p>❌ @apiErrorMessage</p>
|
||||
<div class="error-icon">❌</div>
|
||||
<p>@apiErrorMessage</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -132,6 +174,7 @@
|
||||
@if (isLoadingZip)
|
||||
{
|
||||
<span>Preparing ZIP...</span>
|
||||
<div class="loading-spinner">⏳</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -198,10 +241,17 @@
|
||||
private bool isLoadingZip = false;
|
||||
private int progressPercentage = 0;
|
||||
private string progressMessage = "";
|
||||
private string? currentStageDetails = null;
|
||||
private List<DownloadItem> downloadLinks = new();
|
||||
private YouTubeVideoDetails? currentVideoDetails = null;
|
||||
private string currentSessionId = "";
|
||||
private SessionZipInfo? sessionZipInfo = null;
|
||||
|
||||
// Enhanced progress tracking
|
||||
private DateTime processingStartTime;
|
||||
private TimeSpan? estimatedTimeRemaining;
|
||||
private List<ProgressStage> progressStages = new();
|
||||
private string currentProgressStage = "initializing";
|
||||
|
||||
public class DownloadItem
|
||||
{
|
||||
@@ -211,8 +261,21 @@
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class ProgressStage
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string Label { get; set; } = "";
|
||||
public string Icon { get; set; } = "";
|
||||
public bool IsCompleted { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public int MinProgress { get; set; }
|
||||
public int MaxProgress { get; set; }
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
InitializeProgressStages();
|
||||
|
||||
try
|
||||
{
|
||||
// Initialize session on component load
|
||||
@@ -231,6 +294,109 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeProgressStages()
|
||||
{
|
||||
progressStages = new List<ProgressStage>
|
||||
{
|
||||
new() { Id = "initializing", Label = "Initializing", Icon = "🚀", MinProgress = 0, MaxProgress = 15 },
|
||||
new() { Id = "fetching", Label = "Fetching Video", Icon = "📡", MinProgress = 15, MaxProgress = 35 },
|
||||
new() { Id = "extracting", Label = "Extracting Audio", Icon = "🔄", MinProgress = 35, MaxProgress = 70 },
|
||||
new() { Id = "converting", Label = "Converting to MP3", Icon = "🎵", MinProgress = 70, MaxProgress = 90 },
|
||||
new() { Id = "finalizing", Label = "Finalizing", Icon = "✨", MinProgress = 90, MaxProgress = 100 }
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateProgressStage(int percentage)
|
||||
{
|
||||
foreach (var stage in progressStages)
|
||||
{
|
||||
stage.IsCompleted = percentage > stage.MaxProgress;
|
||||
stage.IsActive = percentage >= stage.MinProgress && percentage <= stage.MaxProgress;
|
||||
}
|
||||
|
||||
var activeStage = progressStages.FirstOrDefault(s => s.IsActive);
|
||||
if (activeStage != null && currentProgressStage != activeStage.Id)
|
||||
{
|
||||
currentProgressStage = activeStage.Id;
|
||||
UpdateStageDetails(activeStage.Id, percentage);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStageDetails(string stageId, int percentage)
|
||||
{
|
||||
currentStageDetails = stageId switch
|
||||
{
|
||||
"initializing" => "Preparing download request and validating URL...",
|
||||
"fetching" => $"Analyzing video content and metadata... ({percentage}%)",
|
||||
"extracting" => $"Extracting high-quality audio stream... ({percentage}%)",
|
||||
"converting" => $"Converting to MP3 format with optimal settings... ({percentage}%)",
|
||||
"finalizing" => $"Completing conversion and preparing download... ({percentage}%)",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private string GetProgressStageTitle()
|
||||
{
|
||||
var activeStage = progressStages.FirstOrDefault(s => s.IsActive);
|
||||
return activeStage?.Label ?? "Processing";
|
||||
}
|
||||
|
||||
private string GetProgressIcon()
|
||||
{
|
||||
var activeStage = progressStages.FirstOrDefault(s => s.IsActive);
|
||||
return activeStage?.Icon ?? "🎵";
|
||||
}
|
||||
|
||||
private string GetProgressIconClass()
|
||||
{
|
||||
if (progressPercentage < 25) return "spinning";
|
||||
if (progressPercentage < 50) return "pulse";
|
||||
if (progressPercentage < 75) return "bounce";
|
||||
if (progressPercentage < 100) return "glow";
|
||||
return "success";
|
||||
}
|
||||
|
||||
private string GetStageClass(ProgressStage stage)
|
||||
{
|
||||
if (stage.IsCompleted) return "completed";
|
||||
if (stage.IsActive) return "active";
|
||||
return "pending";
|
||||
}
|
||||
|
||||
private string GetElapsedTime()
|
||||
{
|
||||
var elapsed = DateTime.Now - processingStartTime;
|
||||
return elapsed.TotalMinutes >= 1
|
||||
? $"{elapsed.Minutes:D2}:{elapsed.Seconds:D2}"
|
||||
: $"{elapsed.Seconds}s";
|
||||
}
|
||||
|
||||
private string GetEstimatedTimeRemaining()
|
||||
{
|
||||
if (!estimatedTimeRemaining.HasValue) return "";
|
||||
|
||||
var eta = estimatedTimeRemaining.Value;
|
||||
return eta.TotalMinutes >= 1
|
||||
? $"{eta.Minutes:D2}:{eta.Seconds:D2}"
|
||||
: $"{eta.Seconds}s";
|
||||
}
|
||||
|
||||
private void CalculateEstimatedTime(int currentProgress)
|
||||
{
|
||||
if (currentProgress > 5) // Only estimate after some progress
|
||||
{
|
||||
var elapsed = DateTime.Now - processingStartTime;
|
||||
var progressRate = currentProgress / elapsed.TotalSeconds;
|
||||
var remainingProgress = 100 - currentProgress;
|
||||
var estimatedSeconds = remainingProgress / progressRate;
|
||||
|
||||
if (estimatedSeconds > 0 && estimatedSeconds < 300) // Cap at 5 minutes
|
||||
{
|
||||
estimatedTimeRemaining = TimeSpan.FromSeconds(estimatedSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadSessionZipInfoAsync()
|
||||
{
|
||||
try
|
||||
@@ -292,6 +458,17 @@
|
||||
outputMessage = "";
|
||||
apiErrorMessage = "";
|
||||
currentVideoDetails = null;
|
||||
processingStartTime = DateTime.Now;
|
||||
estimatedTimeRemaining = null;
|
||||
currentStageDetails = null;
|
||||
|
||||
// Reset progress stages
|
||||
foreach (var stage in progressStages)
|
||||
{
|
||||
stage.IsCompleted = false;
|
||||
stage.IsActive = false;
|
||||
}
|
||||
progressStages.First().IsActive = true;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -304,6 +481,7 @@
|
||||
// Step 1: Start processing (which includes video details fetching)
|
||||
progressMessage = "Starting video processing...";
|
||||
progressPercentage = 5;
|
||||
UpdateProgressStage(progressPercentage);
|
||||
StateHasChanged();
|
||||
|
||||
var request = new ProcessVideoRequest
|
||||
@@ -357,6 +535,7 @@
|
||||
bool videoDetailsReceived = false;
|
||||
int maxAttempts = 60; // Maximum polling attempts (about 2 minutes)
|
||||
int attemptCount = 0;
|
||||
int lastProgress = 0;
|
||||
|
||||
while (attemptCount < maxAttempts && isProcessing)
|
||||
{
|
||||
@@ -364,8 +543,15 @@
|
||||
|
||||
if (status != null)
|
||||
{
|
||||
// Update progress
|
||||
progressPercentage = status.PercentageComplete;
|
||||
// Update progress only if it has increased (prevent UI flicker)
|
||||
if (status.PercentageComplete > lastProgress)
|
||||
{
|
||||
progressPercentage = status.PercentageComplete;
|
||||
lastProgress = progressPercentage;
|
||||
UpdateProgressStage(progressPercentage);
|
||||
CalculateEstimatedTime(progressPercentage);
|
||||
}
|
||||
|
||||
progressMessage = status.Message ?? "Processing...";
|
||||
|
||||
// Check if we received video details
|
||||
@@ -428,6 +614,11 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ensure we reach 100% completion
|
||||
progressPercentage = 100;
|
||||
progressMessage = "Complete!";
|
||||
UpdateProgressStage(progressPercentage);
|
||||
|
||||
// Use the video details from the result if we don't have them yet
|
||||
if (currentVideoDetails == null && result.VideoDetails != null)
|
||||
{
|
||||
@@ -458,15 +649,15 @@
|
||||
CreatedAt = DateTime.Now
|
||||
});
|
||||
|
||||
outputMessage = $"✅ Successfully converted: {title}";
|
||||
progressPercentage = 100;
|
||||
progressMessage = "Complete!";
|
||||
var processingTime = DateTime.Now - processingStartTime;
|
||||
outputMessage = $"Successfully converted: {title} (took {processingTime.TotalSeconds:F1}s)";
|
||||
|
||||
// Reload session ZIP info after successful download
|
||||
await LoadSessionZipInfoAsync();
|
||||
|
||||
// Small delay to show completion
|
||||
await Task.Delay(500);
|
||||
// Show completion for a moment
|
||||
StateHasChanged();
|
||||
await Task.Delay(1000);
|
||||
|
||||
isProcessing = false;
|
||||
youtubeUrl = "";
|
||||
|
||||
Reference in New Issue
Block a user