Add DI, config, and API service for Blazor frontend
Introduce configuration classes and environment-specific settings files. Add DI extensions for registering API/app settings and HttpClient. Implement ITuneRipperApiService with robust error handling and logging. Update Program.cs for config loading and startup logging. Add usage documentation and update imports for new services and extensions. Improves maintainability and developer experience.
This commit is contained in:
@@ -5,7 +5,21 @@ public class ApiSettings
|
|||||||
public const string SectionName = "ApiSettings";
|
public const string SectionName = "ApiSettings";
|
||||||
|
|
||||||
public string? BaseUrl { get; set; }
|
public string? BaseUrl { get; set; }
|
||||||
|
public string? HttpBaseUrl { get; set; }
|
||||||
public string? ApiKey { get; set; }
|
public string? ApiKey { get; set; }
|
||||||
public int TimeoutSeconds { get; set; } = 30;
|
public int TimeoutSeconds { get; set; } = 30;
|
||||||
public bool EnableLogging { get; set; } = true;
|
public bool EnableLogging { get; set; } = true;
|
||||||
|
public int RetryAttempts { get; set; } = 3;
|
||||||
|
public int RetryDelaySeconds { get; set; } = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
public const string SectionName = "AppSettings";
|
||||||
|
|
||||||
|
public string? AppName { get; set; }
|
||||||
|
public string? Version { get; set; }
|
||||||
|
public string? Environment { get; set; }
|
||||||
|
public bool EnableDetailedErrors { get; set; } = true;
|
||||||
|
public bool DebugMode { get; set; } = false;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
# TuneRipper API Service Usage Examples
|
||||||
|
|
||||||
|
## Injecting the Service
|
||||||
|
|
||||||
|
In any Razor component, inject the API service:
|
||||||
|
|
||||||
|
```razor
|
||||||
|
@inject ITuneRipperApiService ApiService
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### 1. Get Video Details
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private async Task GetVideoDetailsExample()
|
||||||
|
{
|
||||||
|
var url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
|
||||||
|
var videoDetails = await ApiService.GetVideoDetailsAsync(url);
|
||||||
|
|
||||||
|
if (videoDetails != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Title: {videoDetails.Title}");
|
||||||
|
Console.WriteLine($"Duration: {videoDetails.Duration}");
|
||||||
|
Console.WriteLine($"Author: {videoDetails.Author}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Start Video Processing
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private async Task StartProcessingExample()
|
||||||
|
{
|
||||||
|
var request = new ProcessVideoRequest
|
||||||
|
{
|
||||||
|
Url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||||
|
Format = "mp3",
|
||||||
|
Quality = "128"
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await ApiService.StartProcessingAsync(request);
|
||||||
|
|
||||||
|
if (response?.Success == true)
|
||||||
|
{
|
||||||
|
var jobId = response.FileName; // This contains the job ID
|
||||||
|
await MonitorProcessing(jobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Monitor Processing Status
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private async Task MonitorProcessing(string jobId)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var status = await ApiService.GetProcessingStatusAsync(jobId);
|
||||||
|
|
||||||
|
if (status == null) break;
|
||||||
|
|
||||||
|
Console.WriteLine($"Status: {status.Status} - {status.PercentageComplete}%");
|
||||||
|
|
||||||
|
if (status.Status == "Completed" || status.Status == "Failed")
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(2000); // Wait 2 seconds before checking again
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get final result
|
||||||
|
var result = await ApiService.GetProcessingResultAsync(jobId);
|
||||||
|
if (result?.Success == true)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Download URL: {result.DownloadUrl}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Download File
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private async Task DownloadFileExample(string jobId)
|
||||||
|
{
|
||||||
|
var fileBytes = await ApiService.DownloadFileAsync(jobId);
|
||||||
|
|
||||||
|
if (fileBytes != null)
|
||||||
|
{
|
||||||
|
// In a Blazor component, you might trigger a download
|
||||||
|
// This would require additional JavaScript interop
|
||||||
|
Console.WriteLine($"Downloaded {fileBytes.Length} bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Check API Health
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private async Task CheckApiHealthExample()
|
||||||
|
{
|
||||||
|
var isHealthy = await ApiService.IsApiHealthyAsync();
|
||||||
|
|
||||||
|
if (isHealthy)
|
||||||
|
{
|
||||||
|
Console.WriteLine("API is healthy and responding");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("API is not responding or unhealthy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Error Handling
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private async Task ErrorHandlingExample()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var videoDetails = await ApiService.GetVideoDetailsAsync("invalid-url");
|
||||||
|
|
||||||
|
if (videoDetails == null)
|
||||||
|
{
|
||||||
|
// Handle case where API returns null (error occurred)
|
||||||
|
Console.WriteLine("Failed to get video details");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Handle unexpected exceptions
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The API service automatically uses the configuration from `appsettings.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ApiSettings": {
|
||||||
|
"BaseUrl": "https://localhost:7160",
|
||||||
|
"HttpBaseUrl": "http://localhost:5112",
|
||||||
|
"TimeoutSeconds": 30,
|
||||||
|
"EnableLogging": true,
|
||||||
|
"RetryAttempts": 3,
|
||||||
|
"RetryDelaySeconds": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Component Example
|
||||||
|
|
||||||
|
```razor
|
||||||
|
@page "/example"
|
||||||
|
@inject ITuneRipperApiService ApiService
|
||||||
|
|
||||||
|
<h3>API Service Example</h3>
|
||||||
|
|
||||||
|
<button @onclick="TestApi">Test API</button>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(message))
|
||||||
|
{
|
||||||
|
<p>@message</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string message = "";
|
||||||
|
|
||||||
|
private async Task TestApi()
|
||||||
|
{
|
||||||
|
message = "Testing API...";
|
||||||
|
|
||||||
|
var isHealthy = await ApiService.IsApiHealthyAsync();
|
||||||
|
|
||||||
|
if (isHealthy)
|
||||||
|
{
|
||||||
|
message = "API is healthy!";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = "API is not responding";
|
||||||
|
}
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using TuneRipper.Shared.Configuration;
|
||||||
|
|
||||||
|
namespace TuneRipper.Extensions;
|
||||||
|
|
||||||
|
public static class ConfigurationExtensions
|
||||||
|
{
|
||||||
|
public static ApiSettings GetApiSettings(this IConfiguration configuration)
|
||||||
|
{
|
||||||
|
return configuration.GetSection(ApiSettings.SectionName).Get<ApiSettings>() ?? new ApiSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppSettings GetAppSettings(this IConfiguration configuration)
|
||||||
|
{
|
||||||
|
return configuration.GetSection(AppSettings.SectionName).Get<AppSettings>() ?? new AppSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using TuneRipper.Services;
|
||||||
|
using TuneRipper.Shared.Configuration;
|
||||||
|
|
||||||
|
namespace TuneRipper.Extensions;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddTuneRipperServices(
|
||||||
|
this IServiceCollection services,
|
||||||
|
IConfiguration configuration)
|
||||||
|
{
|
||||||
|
// Configure settings
|
||||||
|
services.Configure<ApiSettings>(configuration.GetSection(ApiSettings.SectionName));
|
||||||
|
services.Configure<AppSettings>(configuration.GetSection(AppSettings.SectionName));
|
||||||
|
|
||||||
|
// Configure HttpClient for API calls
|
||||||
|
services.AddScoped(sp =>
|
||||||
|
{
|
||||||
|
var apiSettings = configuration.GetSection(ApiSettings.SectionName).Get<ApiSettings>();
|
||||||
|
var client = new HttpClient();
|
||||||
|
|
||||||
|
// Try HTTPS first, fallback to HTTP if configured
|
||||||
|
if (!string.IsNullOrEmpty(apiSettings?.BaseUrl))
|
||||||
|
{
|
||||||
|
client.BaseAddress = new Uri(apiSettings.BaseUrl);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(apiSettings?.HttpBaseUrl))
|
||||||
|
{
|
||||||
|
client.BaseAddress = new Uri(apiSettings.HttpBaseUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default fallback
|
||||||
|
client.BaseAddress = new Uri("https://localhost:7160");
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(apiSettings?.TimeoutSeconds ?? 30);
|
||||||
|
|
||||||
|
return client;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register API service
|
||||||
|
services.AddScoped<ITuneRipperApiService, TuneRipperApiService>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-2
@@ -1,11 +1,32 @@
|
|||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
using TuneRipper;
|
using TuneRipper;
|
||||||
|
using TuneRipper.Extensions;
|
||||||
|
using TuneRipper.Shared.Configuration;
|
||||||
|
|
||||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
builder.RootComponents.Add<App>("#app");
|
builder.RootComponents.Add<App>("#app");
|
||||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||||
|
|
||||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
// Add configuration
|
||||||
|
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile($"appsettings.{builder.HostEnvironment.Environment}.json", optional: true, reloadOnChange: true);
|
||||||
|
|
||||||
await builder.Build().RunAsync();
|
// Add logging
|
||||||
|
builder.Logging.SetMinimumLevel(LogLevel.Information);
|
||||||
|
|
||||||
|
// Add TuneRipper services (API service, HttpClient, settings)
|
||||||
|
builder.Services.AddTuneRipperServices(builder.Configuration);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Log startup information
|
||||||
|
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||||
|
logger.LogInformation("?? TuneRipper Blazor WebAssembly app starting...");
|
||||||
|
|
||||||
|
var apiSettings = app.Configuration.GetApiSettings();
|
||||||
|
logger.LogInformation("?? API Base URL: {BaseUrl}", apiSettings.BaseUrl);
|
||||||
|
logger.LogInformation("?? API Settings - Timeout: {Timeout}s, Logging: {Logging}",
|
||||||
|
apiSettings.TimeoutSeconds, apiSettings.EnableLogging);
|
||||||
|
|
||||||
|
await app.RunAsync();
|
||||||
|
|||||||
@@ -0,0 +1,367 @@
|
|||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json;
|
||||||
|
using TuneRipper.Shared.Models;
|
||||||
|
using TuneRipper.Shared.Configuration;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace TuneRipper.Services;
|
||||||
|
|
||||||
|
public interface ITuneRipperApiService
|
||||||
|
{
|
||||||
|
Task<YouTubeVideoDetails?> GetVideoDetailsAsync(string url);
|
||||||
|
Task<ProcessVideoResponse?> StartProcessingAsync(ProcessVideoRequest request);
|
||||||
|
Task<DownloadProgress?> GetProcessingStatusAsync(string jobId);
|
||||||
|
Task<ProcessVideoResponse?> GetProcessingResultAsync(string jobId);
|
||||||
|
Task<IEnumerable<JobStatusResponse>?> GetAllJobsAsync();
|
||||||
|
Task<byte[]?> DownloadFileAsync(string jobId);
|
||||||
|
Task<bool> IsApiHealthyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TuneRipperApiService : ITuneRipperApiService
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ApiSettings _apiSettings;
|
||||||
|
private readonly ILogger<TuneRipperApiService> _logger;
|
||||||
|
private readonly JsonSerializerOptions _jsonOptions;
|
||||||
|
|
||||||
|
public TuneRipperApiService(
|
||||||
|
HttpClient httpClient,
|
||||||
|
IOptions<ApiSettings> apiSettings,
|
||||||
|
ILogger<TuneRipperApiService> logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_apiSettings = apiSettings.Value;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
// Configure JSON serialization options
|
||||||
|
_jsonOptions = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure HttpClient
|
||||||
|
_httpClient.BaseAddress = new Uri(_apiSettings.BaseUrl ?? "https://localhost:7160");
|
||||||
|
_httpClient.Timeout = TimeSpan.FromSeconds(_apiSettings.TimeoutSeconds);
|
||||||
|
|
||||||
|
if (_apiSettings.EnableLogging)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("TuneRipperApiService initialized with base URL: {BaseUrl}", _httpClient.BaseAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<YouTubeVideoDetails?> GetVideoDetailsAsync(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("GetVideoDetailsAsync called with empty URL");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Fetching video details for URL: {Url}", url);
|
||||||
|
|
||||||
|
var encodedUrl = Uri.EscapeDataString(url);
|
||||||
|
var response = await _httpClient.GetAsync($"api/video/details?url={encodedUrl}");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var videoDetails = await response.Content.ReadFromJsonAsync<YouTubeVideoDetails>(_jsonOptions);
|
||||||
|
_logger.LogInformation("Successfully retrieved video details for URL: {Url}", url);
|
||||||
|
return videoDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
_logger.LogWarning("Failed to get video details. Status: {StatusCode}, Error: {Error}",
|
||||||
|
response.StatusCode, errorContent);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "HTTP error while fetching video details for URL: {Url}", url);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Timeout while fetching video details for URL: {Url}", url);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unexpected error while fetching video details for URL: {Url}", url);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ProcessVideoResponse?> StartProcessingAsync(ProcessVideoRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (request == null || string.IsNullOrWhiteSpace(request.Url))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("StartProcessingAsync called with invalid request");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Starting video processing for URL: {Url}", request.Url);
|
||||||
|
|
||||||
|
var response = await _httpClient.PostAsJsonAsync("api/video/process", request, _jsonOptions);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<ProcessVideoResponse>(_jsonOptions);
|
||||||
|
_logger.LogInformation("Successfully started processing for URL: {Url}, JobId: {JobId}",
|
||||||
|
request.Url, result?.FileName);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
_logger.LogWarning("Failed to start processing. Status: {StatusCode}, Error: {Error}",
|
||||||
|
response.StatusCode, errorContent);
|
||||||
|
|
||||||
|
return new ProcessVideoResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = $"Failed to start processing: {errorContent}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "HTTP error while starting video processing");
|
||||||
|
return new ProcessVideoResponse { Success = false, Message = "Network error occurred" };
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Timeout while starting video processing");
|
||||||
|
return new ProcessVideoResponse { Success = false, Message = "Request timeout" };
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unexpected error while starting video processing");
|
||||||
|
return new ProcessVideoResponse { Success = false, Message = "An unexpected error occurred" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DownloadProgress?> GetProcessingStatusAsync(string jobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(jobId))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("GetProcessingStatusAsync called with empty jobId");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync($"api/video/status/{jobId}");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var status = await response.Content.ReadFromJsonAsync<DownloadProgress>(_jsonOptions);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Job not found: {JobId}", jobId);
|
||||||
|
return new DownloadProgress
|
||||||
|
{
|
||||||
|
Status = "NotFound",
|
||||||
|
Message = "Job not found",
|
||||||
|
PercentageComplete = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
_logger.LogWarning("Failed to get processing status. Status: {StatusCode}, Error: {Error}",
|
||||||
|
response.StatusCode, errorContent);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "HTTP error while getting processing status for job: {JobId}", jobId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Timeout while getting processing status for job: {JobId}", jobId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unexpected error while getting processing status for job: {JobId}", jobId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ProcessVideoResponse?> GetProcessingResultAsync(string jobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(jobId))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("GetProcessingResultAsync called with empty jobId");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync($"api/video/result/{jobId}");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<ProcessVideoResponse>(_jsonOptions);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.StatusCode == System.Net.HttpStatusCode.Accepted)
|
||||||
|
{
|
||||||
|
return new ProcessVideoResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "Processing not completed yet"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Job not found: {JobId}", jobId);
|
||||||
|
return new ProcessVideoResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "Job not found"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
_logger.LogWarning("Failed to get processing result. Status: {StatusCode}, Error: {Error}",
|
||||||
|
response.StatusCode, errorContent);
|
||||||
|
|
||||||
|
return new ProcessVideoResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = $"Failed to get result: {errorContent}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "HTTP error while getting processing result for job: {JobId}", jobId);
|
||||||
|
return new ProcessVideoResponse { Success = false, Message = "Network error occurred" };
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Timeout while getting processing result for job: {JobId}", jobId);
|
||||||
|
return new ProcessVideoResponse { Success = false, Message = "Request timeout" };
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unexpected error while getting processing result for job: {JobId}", jobId);
|
||||||
|
return new ProcessVideoResponse { Success = false, Message = "An unexpected error occurred" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<JobStatusResponse>?> GetAllJobsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Fetching all jobs");
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync("api/video/jobs");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var jobs = await response.Content.ReadFromJsonAsync<IEnumerable<JobStatusResponse>>(_jsonOptions);
|
||||||
|
_logger.LogInformation("Successfully retrieved {Count} jobs", jobs?.Count() ?? 0);
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
_logger.LogWarning("Failed to get jobs. Status: {StatusCode}, Error: {Error}",
|
||||||
|
response.StatusCode, errorContent);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "HTTP error while getting all jobs");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Timeout while getting all jobs");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unexpected error while getting all jobs");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<byte[]?> DownloadFileAsync(string jobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(jobId))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("DownloadFileAsync called with empty jobId");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Downloading file for job: {JobId}", jobId);
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync($"api/download/{jobId}");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var fileBytes = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
_logger.LogInformation("Successfully downloaded file for job: {JobId}, Size: {Size} bytes",
|
||||||
|
jobId, fileBytes.Length);
|
||||||
|
return fileBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
_logger.LogWarning("Failed to download file. Status: {StatusCode}, Error: {Error}",
|
||||||
|
response.StatusCode, errorContent);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "HTTP error while downloading file for job: {JobId}", jobId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Timeout while downloading file for job: {JobId}", jobId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unexpected error while downloading file for job: {JobId}", jobId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsApiHealthyAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.GetAsync("api/health");
|
||||||
|
var isHealthy = response.IsSuccessStatusCode;
|
||||||
|
|
||||||
|
if (_apiSettings.EnableLogging)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("API health check result: {IsHealthy}", isHealthy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isHealthy;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error during API health check");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,3 +8,7 @@
|
|||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using TuneRipper
|
@using TuneRipper
|
||||||
@using TuneRipper.Layout
|
@using TuneRipper.Layout
|
||||||
|
@using TuneRipper.Services
|
||||||
|
@using TuneRipper.Extensions
|
||||||
|
@using TuneRipper.Shared.Models
|
||||||
|
@using TuneRipper.Shared.Configuration
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"ApiSettings": {
|
||||||
|
"BaseUrl": "https://localhost:7160",
|
||||||
|
"HttpBaseUrl": "http://localhost:5112",
|
||||||
|
"TimeoutSeconds": 60,
|
||||||
|
"EnableLogging": true,
|
||||||
|
"RetryAttempts": 3,
|
||||||
|
"RetryDelaySeconds": 1
|
||||||
|
},
|
||||||
|
"AppSettings": {
|
||||||
|
"Environment": "Development",
|
||||||
|
"EnableDetailedErrors": true,
|
||||||
|
"DebugMode": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"ApiSettings": {
|
||||||
|
"BaseUrl": "https://localhost:7160",
|
||||||
|
"HttpBaseUrl": "http://localhost:5112",
|
||||||
|
"TimeoutSeconds": 30,
|
||||||
|
"EnableLogging": true,
|
||||||
|
"RetryAttempts": 3,
|
||||||
|
"RetryDelaySeconds": 2
|
||||||
|
},
|
||||||
|
"AppSettings": {
|
||||||
|
"AppName": "TuneRipper",
|
||||||
|
"Version": "1.0.0",
|
||||||
|
"Environment": "Development",
|
||||||
|
"EnableDetailedErrors": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user