using System.Net.Http;
using System.Text.Json;
namespace VoiceHubLanDesktop;
///
/// VoiceHub API 服务
///
public sealed class VoiceHubApiService : IDisposable
{
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonOptions;
private const string DefaultApiUrl = "https://voicehub.lao-shui.top/api/songs/public";
private const int MaxRetryCount = 3;
private readonly TimeSpan _requestTimeout = TimeSpan.FromSeconds(10);
public VoiceHubApiService()
{
_httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};
_jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
}
public async Task>> GetPublicScheduleAsync(
string? apiUrl = null,
CancellationToken cancellationToken = default)
{
var url = string.IsNullOrWhiteSpace(apiUrl) ? DefaultApiUrl : apiUrl.Trim();
for (var attempt = 0; attempt < MaxRetryCount; attempt++)
{
try
{
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
cts.CancelAfter(_requestTimeout);
var jsonResponse = await _httpClient.GetStringAsync(url, cts.Token);
var items = JsonSerializer.Deserialize>(jsonResponse, _jsonOptions);
if (items is null)
{
return ApiResult>.Failure("数据解析失败");
}
return ApiResult>.Success(items);
}
catch (HttpRequestException ex)
{
if (attempt == MaxRetryCount - 1)
{
return ApiResult>.Failure($"网络错误: {ex.Message}");
}
}
catch (TaskCanceledException)
{
if (attempt == MaxRetryCount - 1)
{
return ApiResult>.Failure("请求超时");
}
}
catch (JsonException ex)
{
return ApiResult>.Failure($"数据格式错误: {ex.Message}");
}
catch (Exception ex)
{
return ApiResult>.Failure($"未知错误: {ex.Message}");
}
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)), cancellationToken);
}
return ApiResult>.Failure("获取数据失败");
}
public void Dispose() => _httpClient.Dispose();
}
public sealed class ApiResult
{
public bool IsSuccess { get; }
public T? Data { get; }
public string? ErrorMessage { get; }
private ApiResult(bool isSuccess, T? data, string? errorMessage)
{
IsSuccess = isSuccess;
Data = data;
ErrorMessage = errorMessage;
}
public static ApiResult Success(T data) => new(true, data, null);
public static ApiResult Failure(string errorMessage) => new(false, default, errorMessage);
}