using System.Net; using System.Net.Http.Json; using DeepDrftModels.DTOs; using Microsoft.Extensions.DependencyInjection; using NetBlocks.Models; namespace DeepDrftPublic.Client.Clients; public class TrackMediaResponse : IDisposable { public Stream Stream { get; } public long ContentLength { get; } public TrackMediaResponse(Stream stream, long contentLength) { Stream = stream; ContentLength = contentLength; } public void Dispose() { Stream?.Dispose(); } } public class TrackMediaClient { private readonly HttpClient _http; public TrackMediaClient(IHttpClientFactory httpClientFactory) { _http = httpClientFactory.CreateClient("DeepDrft.Content"); } /// /// Fetches the WAV stream for a track, optionally starting from a byte offset. /// The cancellation token is forwarded to so a /// navigation or seek-replacement aborts the in-flight server connection rather /// than leaving the server draining bytes into a dead socket. /// public async Task> GetTrackMedia( string trackId, long byteOffset = 0, CancellationToken cancellationToken = default) { try { // Build URL with optional offset parameter var url = byteOffset > 0 ? $"api/track/{trackId}?offset={byteOffset}" : $"api/track/{trackId}"; // Use HttpCompletionOption.ResponseHeadersRead to get stream immediately var response = await _http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); response.EnsureSuccessStatusCode(); var contentLength = response.Content.Headers.ContentLength ?? 0; var stream = await response.Content.ReadAsStreamAsync(cancellationToken); return ApiResult.CreatePassResult(new TrackMediaResponse(stream, contentLength)); } catch (Exception e) { return ApiResult.CreateFailResult(e.Message); } } /// /// Fetches a track's stored waveform loudness profile. A 404 means no profile is stored /// (existing tracks predate profiling, or computation failed at upload); callers treat that /// as "render a flat seekbar" rather than an error, so it surfaces as a fail result with a /// stable message rather than throwing. /// public async Task> GetWaveformProfileAsync(string trackId, CancellationToken cancellationToken = default) { try { var response = await _http.GetAsync($"api/track/{trackId}/waveform", cancellationToken); if (response.StatusCode == HttpStatusCode.NotFound) { return ApiResult.CreateFailResult("No waveform profile available"); } response.EnsureSuccessStatusCode(); var profile = await response.Content.ReadFromJsonAsync(); if (profile is null) { return ApiResult.CreateFailResult("Waveform profile response was empty"); } return ApiResult.CreatePassResult(profile); } catch (Exception e) { return ApiResult.CreateFailResult(e.Message); } } }