using System.Runtime.CompilerServices; using System.Text.Json; using AIStudio.Tools.Rust; namespace AIStudio.Tools.Services; public partial class RustService { /// Starts a Rust media normalization job. /// Absolute source path. /// Absolute operation-owned output path. /// Request cancellation token. /// The opaque runtime job identifier. public async Task StartMediaJobAsync(string inputPath, string outputPath, CancellationToken token = default) { using var response = await this.http.PostAsJsonAsync( "/media/jobs", new CreateMediaJobRequest(inputPath, outputPath), this.jsonRustSerializerOptions, token); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(this.jsonRustSerializerOptions, token); return result?.JobId ?? throw new InvalidOperationException("The Rust runtime did not return a media job ID."); } /// Streams replayed and live snapshots until the media job becomes terminal. /// Runtime job identifier. /// Stream cancellation token. /// Asynchronous media job snapshots. public async IAsyncEnumerable StreamMediaJobEventsAsync(string jobId, [EnumeratorCancellation] CancellationToken token = default) { using var request = new HttpRequestMessage(HttpMethod.Get, $"/media/jobs/{Uri.EscapeDataString(jobId)}/events"); using var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token); response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(token); using var reader = new StreamReader(stream); while (!token.IsCancellationRequested) { var line = await reader.ReadLineAsync(token); if (line is null) yield break; if (!line.StartsWith("data:", StringComparison.Ordinal)) continue; var json = line["data:".Length..].Trim(); var mediaEvent = JsonSerializer.Deserialize(json, this.jsonRustSerializerOptions); if (mediaEvent is not null) yield return mediaEvent; if (mediaEvent?.Phase is MediaJobPhase.COMPLETED or MediaJobPhase.FAILED or MediaJobPhase.CANCELLED) yield break; } } /// Requests cooperative cancellation of a Rust media job. /// Runtime job identifier. /// Request cancellation token. public async Task CancelMediaJobAsync(string jobId, CancellationToken token = default) { using var response = await this.http.DeleteAsync($"/media/jobs/{Uri.EscapeDataString(jobId)}", token); if (response is { IsSuccessStatusCode: false, StatusCode: not System.Net.HttpStatusCode.NotFound }) response.EnsureSuccessStatusCode(); } }