Merge branch 'waveform-w1-t2-api' into dev
This commit is contained in:
@@ -5,7 +5,9 @@ using DeepDrftContent.Audio;
|
||||
using DeepDrftContent.Constants;
|
||||
using DeepDrftContent.FileDatabase.Services;
|
||||
using DeepDrftContent.FileDatabase.Models;
|
||||
using DeepDrftContent.Processors;
|
||||
using DeepDrftData;
|
||||
using DeepDrftModels.DTOs;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DeepDrftAPI.Controllers;
|
||||
@@ -18,6 +20,7 @@ public class TrackController : ControllerBase
|
||||
private readonly WavOffsetService _wavOffsetService;
|
||||
private readonly UnifiedTrackService _unifiedService;
|
||||
private readonly ITrackService _sqlTrackService;
|
||||
private readonly WaveformProfileService _waveformProfileService;
|
||||
private readonly ILogger<TrackController> _logger;
|
||||
|
||||
// FileDatabase is injected directly for PutTrack because that endpoint receives a pre-processed
|
||||
@@ -32,6 +35,7 @@ public class TrackController : ControllerBase
|
||||
WavOffsetService wavOffsetService,
|
||||
UnifiedTrackService unifiedService,
|
||||
ITrackService sqlTrackService,
|
||||
WaveformProfileService waveformProfileService,
|
||||
ILogger<TrackController> logger)
|
||||
{
|
||||
_trackContentService = trackContentService;
|
||||
@@ -39,6 +43,7 @@ public class TrackController : ControllerBase
|
||||
_wavOffsetService = wavOffsetService;
|
||||
_unifiedService = unifiedService;
|
||||
_sqlTrackService = sqlTrackService;
|
||||
_waveformProfileService = waveformProfileService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -348,6 +353,28 @@ public class TrackController : ControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
// GET api/track/{trackId}/waveform (unauthenticated)
|
||||
// Returns the stored waveform loudness profile for a track, base64-encoded. Public listener
|
||||
// data, same auth posture as GET api/track/{trackId} streaming. 404 when no profile is stored
|
||||
// (existing tracks predate profiling, or computation failed at upload — the frontend falls back
|
||||
// to a flat seekbar). The "waveform" literal suffix keeps this distinct from the audio route.
|
||||
[HttpGet("{trackId}/waveform")]
|
||||
public async Task<ActionResult> GetWaveform(string trackId)
|
||||
{
|
||||
var bytes = await _waveformProfileService.GetProfileAsync(trackId);
|
||||
if (bytes is null)
|
||||
{
|
||||
_logger.LogInformation("No waveform profile for track: {TrackId}", trackId);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(new WaveformProfileDto
|
||||
{
|
||||
BucketCount = bytes.Length,
|
||||
Data = Convert.ToBase64String(bytes),
|
||||
});
|
||||
}
|
||||
|
||||
[ApiKeyAuthorize]
|
||||
[HttpPut("{trackId}")]
|
||||
public async Task<ActionResult> PutTrack(string trackId, [FromBody] AudioBinaryDto track)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using DeepDrftModels.DTOs;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NetBlocks.Models;
|
||||
|
||||
@@ -61,4 +64,36 @@ public class TrackMediaClient
|
||||
return ApiResult<TrackMediaResponse>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public async Task<ApiResult<WaveformProfileDto>> GetWaveformProfileAsync(string trackId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _http.GetAsync($"api/track/{trackId}/waveform", cancellationToken);
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return ApiResult<WaveformProfileDto>.CreateFailResult("No waveform profile available");
|
||||
}
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var profile = await response.Content.ReadFromJsonAsync<WaveformProfileDto>();
|
||||
if (profile is null)
|
||||
{
|
||||
return ApiResult<WaveformProfileDto>.CreateFailResult("Waveform profile response was empty");
|
||||
}
|
||||
|
||||
return ApiResult<WaveformProfileDto>.CreatePassResult(profile);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ApiResult<WaveformProfileDto>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,4 +106,38 @@ public class TrackProxyController : ControllerBase
|
||||
HttpContext.Response.RegisterForDispose(upstream);
|
||||
return File(stream, contentType, enableRangeProcessing: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proxies a track's stored waveform profile (JSON) from DeepDrftAPI. Unauthenticated,
|
||||
/// same posture as the audio stream forward. The profile is small JSON, so it is buffered
|
||||
/// and relayed rather than streamed; a 404 from upstream (no profile stored) passes through.
|
||||
/// </summary>
|
||||
[HttpGet("{trackId}/waveform")]
|
||||
public async Task<ActionResult> GetWaveform(string trackId, CancellationToken ct = default)
|
||||
{
|
||||
var path = $"api/track/{Uri.EscapeDataString(trackId)}/waveform";
|
||||
|
||||
HttpResponseMessage upstream;
|
||||
try
|
||||
{
|
||||
upstream = await _upstream.GetAsync(path, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Upstream call to DeepDrftAPI track/{TrackId}/waveform failed", trackId);
|
||||
return StatusCode(502, "Upstream unavailable");
|
||||
}
|
||||
|
||||
using (upstream)
|
||||
{
|
||||
if (!upstream.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogWarning("DeepDrftAPI track/{TrackId}/waveform returned {Status}", trackId, (int)upstream.StatusCode);
|
||||
return StatusCode((int)upstream.StatusCode);
|
||||
}
|
||||
|
||||
var json = await upstream.Content.ReadAsStringAsync(ct);
|
||||
return Content(json, "application/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user