Files
ContentGen_BE/media-worker/Services/SunoMusicService.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

138 lines
5.0 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.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SaasMediaWorker.Configuration;
using SaasMediaWorker.Models;
namespace SaasMediaWorker.Services;
/// <summary>
/// Suno AI API Client — Background müzik üretimi.
/// Projenin musicPrompt'unu kullanarak AI müzik üretir.
/// </summary>
public class SunoMusicService
{
private readonly HttpClient _httpClient;
private readonly ILogger<SunoMusicService> _logger;
private readonly ApiSettings _settings;
public SunoMusicService(
HttpClient httpClient,
ILogger<SunoMusicService> logger,
IOptions<ApiSettings> settings)
{
_httpClient = httpClient;
_logger = logger;
_settings = settings.Value;
_httpClient.BaseAddress = new Uri(_settings.SunoBaseUrl);
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_settings.SunoApiKey}");
_httpClient.Timeout = TimeSpan.FromMinutes(5);
}
/// <summary>
/// Proje için background müzik üretir ve dosyaya kaydeder.
/// </summary>
public async Task<GeneratedMediaFile> GenerateMusicAsync(
string musicPrompt,
int targetDurationSeconds,
string outputDirectory,
CancellationToken ct)
{
_logger.LogInformation(
"🎵 Müzik üretimi — Prompt: \"{Prompt}\", Süre: {Duration}s",
musicPrompt[..Math.Min(80, musicPrompt.Length)],
targetDurationSeconds);
var requestBody = new
{
prompt = musicPrompt,
duration = Math.Min(120, targetDurationSeconds + 5), // Biraz fazla üret (fade-out için)
make_instrumental = true, // Vokal olmadan
model = "chirp-v3"
};
var content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json");
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
var generationId = result.GetProperty("id").GetString()
?? throw new InvalidOperationException("Suno generation ID alınamadı");
_logger.LogInformation("Suno generation başlatıldı: {Id}", generationId);
// Polling — müzik hazır olana kadar bekle
var musicUrl = await PollForCompletionAsync(generationId, ct);
// Müzik dosyasını indir
var outputPath = Path.Combine(outputDirectory, "background_music.mp3");
await DownloadFileAsync(musicUrl, outputPath, ct);
var fileInfo = new FileInfo(outputPath);
_logger.LogInformation("Müzik indirildi: {Path} ({Size} bytes)", outputPath, fileInfo.Length);
return new GeneratedMediaFile
{
SceneId = string.Empty, // Proje seviyesi — sahneye bağlı değil
SceneOrder = 0,
Type = MediaFileType.AudioMusic,
LocalPath = outputPath,
FileSizeBytes = fileInfo.Length,
DurationSeconds = targetDurationSeconds,
MimeType = "audio/mpeg",
AiProvider = "suno"
};
}
private async Task<string> PollForCompletionAsync(string generationId, CancellationToken ct)
{
var maxAttempts = 60; // 5 dakika (5s × 60)
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" || status == "complete")
{
return result.GetProperty("audio_url").GetString()
?? throw new InvalidOperationException("Müzik URL bulunamadı");
}
if (status == "failed" || status == "error")
{
var errorMsg = result.TryGetProperty("error", out var err)
? err.GetString() : "Bilinmeyen hata";
throw new InvalidOperationException($"Suno müzik üretimi başarısız: {errorMsg}");
}
}
throw new TimeoutException($"Suno müzik ü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);
}
}