Streamline video conversion workflow & UI integration

Removed /api/video/details endpoint; video details now fetched as first step of processing and included in progress/status responses. Updated backend models and controller logic to support integrated workflow and improved error handling. Refactored frontend to show video info and conversion progress in real time, removed legacy API calls, and enhanced UI/UX with responsive video info cards and progress bars. Updated documentation and example code to reflect new workflow.
This commit is contained in:
Michal Vrba
2025-12-11 14:59:14 +01:00
parent 8bfcc3617b
commit 160b7e7115
5 changed files with 752 additions and 174 deletions
+44 -45
View File
@@ -21,6 +21,7 @@ public class VideoController : ControllerBase
public ProcessVideoRequest Request { get; set; } = new();
public DownloadProgress Progress { get; set; } = new();
public ProcessVideoResponse? Result { get; set; }
public YouTubeVideoDetails? VideoDetails { get; set; } // Add video details to job
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
@@ -30,39 +31,6 @@ public class VideoController : ControllerBase
_logger = logger;
}
[HttpGet("details")]
public async Task<ActionResult<YouTubeVideoDetails>> GetVideoDetails([FromQuery] string url)
{
if (string.IsNullOrWhiteSpace(url))
{
return BadRequest("URL is required");
}
if (!_videoService.IsValidYouTubeUrl(url))
{
return BadRequest("Invalid YouTube URL");
}
try
{
_logger.LogInformation("Fetching video details for URL: {Url}", url);
var videoDetails = await _videoService.GetVideoDetailsAsync(url);
if (videoDetails == null)
{
return NotFound("Video not found or unavailable");
}
return Ok(videoDetails);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching video details for URL: {Url}", url);
return StatusCode(500, $"Error fetching video details: {ex.Message}");
}
}
[HttpPost("process")]
public async Task<ActionResult<ProcessVideoResponse>> StartProcessing([FromBody] ProcessVideoRequest request)
{
@@ -132,7 +100,22 @@ public class VideoController : ControllerBase
return NotFound("Job not found");
}
return Ok(job.Progress);
// Return progress with video details if available
var progress = job.Progress;
// If we have video details and we're past the first step, include them in a custom response
if (job.VideoDetails != null && job.Progress.PercentageComplete >= 15)
{
return Ok(new
{
PercentageComplete = progress.PercentageComplete,
Status = progress.Status,
Message = progress.Message,
VideoDetails = job.VideoDetails
});
}
return Ok(progress);
}
[HttpGet("result/{jobId}")]
@@ -182,19 +165,38 @@ public class VideoController : ControllerBase
{
_logger.LogInformation("Processing video for job {JobId}", jobId);
// Simulate video processing steps
var steps = new[]
// Step 1: Fetch video details (NEW FIRST STEP)
job.Progress.Status = "Processing";
job.Progress.Message = "Fetching video details...";
job.Progress.PercentageComplete = 5;
_logger.LogInformation("Job {JobId}: Fetching video details", jobId);
var videoDetails = await _videoService.GetVideoDetailsAsync(job.Request.Url!);
if (videoDetails == null)
{
("Fetching video information", 10),
("Analyzing audio stream", 25),
throw new Exception("Unable to fetch video details. Invalid URL or video unavailable.");
}
// Store video details in the job
job.VideoDetails = videoDetails;
job.Progress.Message = "Video details retrieved successfully";
job.Progress.PercentageComplete = 15;
_logger.LogInformation("Job {JobId}: Video details retrieved - Title: {Title}", jobId, videoDetails.Title);
// Continue with remaining processing steps
var remainingSteps = new[]
{
("Analyzing audio stream", 30),
("Downloading audio data", 60),
("Converting to MP3", 85),
("Finalizing", 100)
};
foreach (var (message, percentage) in steps)
foreach (var (message, percentage) in remainingSteps)
{
job.Progress.Status = "Processing";
job.Progress.Message = message;
job.Progress.PercentageComplete = percentage;
@@ -204,9 +206,6 @@ public class VideoController : ControllerBase
await Task.Delay(Random.Shared.Next(1000, 3000));
}
// Get video details using the service
var videoDetails = await _videoService.GetVideoDetailsAsync(job.Request.Url!);
// Complete processing
job.Progress.Status = "Completed";
job.Progress.Message = "Processing completed successfully";
@@ -216,7 +215,7 @@ public class VideoController : ControllerBase
{
Success = true,
Message = "Video processed successfully",
FileName = $"{videoDetails?.Title?.Replace(" ", "_") ?? "video"}.{job.Request.Format ?? "mp3"}",
FileName = $"{videoDetails.Title?.Replace(" ", "_") ?? "video"}.{job.Request.Format ?? "mp3"}",
DownloadUrl = $"/api/download/{jobId}",
VideoDetails = videoDetails
};
@@ -237,7 +236,7 @@ public class VideoController : ControllerBase
Message = ex.Message,
FileName = null,
DownloadUrl = null,
VideoDetails = null
VideoDetails = job.VideoDetails // Include video details even on failure if we got them
};
}
}