Files
ContentGen_BE/media-worker/Services/HiggsFieldService.cs
Harun CAN 85c35c73e8
Some checks failed
Backend Deploy 🚀 / build-and-deploy (push) Has been cancelled
main
2026-03-29 12:43:49 +03:00

155 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SaasMediaWorker.Configuration;
using SaasMediaWorker.Models;
namespace SaasMediaWorker.Services;
/// <summary>
/// Higgsfield AI API Client — Video klip üretimi.
/// Her sahnenin görsel prompt'unu Higgsfield'a gönderip video dosyası indirir.
/// </summary>
public class HiggsFieldService
{
private readonly HttpClient _httpClient;
private readonly ILogger<HiggsFieldService> _logger;
private readonly ApiSettings _settings;
public HiggsFieldService(
HttpClient httpClient,
ILogger<HiggsFieldService> logger,
IOptions<ApiSettings> settings)
{
_httpClient = httpClient;
_logger = logger;
_settings = settings.Value;
_httpClient.BaseAddress = new Uri(_settings.HiggsFieldBaseUrl);
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", _settings.HiggsFieldApiKey);
_httpClient.Timeout = TimeSpan.FromMinutes(5);
}
/// <summary>
/// Bir sahne için video klip üretir ve dosyayı belirtilen dizine indirir.
/// </summary>
public async Task<GeneratedMediaFile> GenerateVideoClipAsync(
ScenePayload scene,
string outputDirectory,
string aspectRatio,
CancellationToken ct)
{
_logger.LogInformation(
"🎥 Video üretimi — Sahne {Order}: {Prompt}",
scene.Order, scene.VisualPrompt[..Math.Min(80, scene.VisualPrompt.Length)]);
var requestBody = new
{
prompt = scene.VisualPrompt,
duration = Math.Max(3, Math.Min(10, (int)scene.Duration)),
aspect_ratio = MapAspectRatio(aspectRatio),
style = "cinematic",
quality = "high"
};
var content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json");
// Video üretim isteği gönder
var response = await _httpClient.PostAsync("/generations", content, ct);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync(ct);
var result = JsonSerializer.Deserialize<JsonElement>(responseJson);
// Generation ID al ve polling ile sonucu bekle
var generationId = result.GetProperty("id").GetString()
?? throw new InvalidOperationException("Generation ID alınamadı");
_logger.LogInformation("Higgsfield generation başlatıldı: {Id}", generationId);
// Polling — video hazır olana kadar bekle
var videoUrl = await PollForCompletionAsync(generationId, ct);
// Video dosyasını indir
var outputPath = Path.Combine(outputDirectory, $"scene_{scene.Order:D2}_video.mp4");
await DownloadFileAsync(videoUrl, outputPath, ct);
var fileInfo = new FileInfo(outputPath);
return new GeneratedMediaFile
{
SceneId = scene.Id,
SceneOrder = scene.Order,
Type = MediaFileType.VideoClip,
LocalPath = outputPath,
FileSizeBytes = fileInfo.Length,
DurationSeconds = scene.Duration,
MimeType = "video/mp4",
AiProvider = "higgsfield"
};
}
private async Task<string> PollForCompletionAsync(string generationId, CancellationToken ct)
{
var maxAttempts = 120; // 10 dakika (5s interval × 120)
for (var i = 0; i < maxAttempts; i++)
{
ct.ThrowIfCancellationRequested();
await Task.Delay(5000, ct);
var response = await _httpClient.GetAsync($"/generations/{generationId}", ct);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync(ct);
var result = JsonSerializer.Deserialize<JsonElement>(json);
var status = result.GetProperty("status").GetString();
if (status == "completed")
{
return result.GetProperty("video_url").GetString()
?? throw new InvalidOperationException("Video URL bulunamadı");
}
if (status == "failed")
{
var errorMsg = result.TryGetProperty("error", out var err)
? err.GetString() : "Bilinmeyen hata";
throw new InvalidOperationException($"Higgsfield video üretimi başarısız: {errorMsg}");
}
if (i % 6 == 0) // Her 30 saniyede bir log
{
_logger.LogInformation("Higgsfield polling — {Id}: {Status}", generationId, status);
}
}
throw new TimeoutException($"Higgsfield video üretimi zaman aşımına uğradı: {generationId}");
}
private async Task DownloadFileAsync(string url, string outputPath, CancellationToken ct)
{
using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, ct);
response.EnsureSuccessStatusCode();
await using var fileStream = File.Create(outputPath);
await response.Content.CopyToAsync(fileStream, ct);
_logger.LogInformation("Video indirildi: {Path} ({Size} bytes)",
outputPath, new FileInfo(outputPath).Length);
}
private static string MapAspectRatio(string aspectRatio) => aspectRatio switch
{
"PORTRAIT_9_16" => "9:16",
"SQUARE_1_1" => "1:1",
"LANDSCAPE_16_9" => "16:9",
_ => "9:16"
};
}