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; /// /// Minimax TTS API Client — Metin → Ses dönüşümü. /// public class MinimaxTtsService { private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly ApiSettings _settings; public MinimaxTtsService( HttpClient httpClient, ILogger logger, IOptions settings) { _httpClient = httpClient; _logger = logger; _settings = settings.Value; _httpClient.BaseAddress = new Uri("https://api.minimax.chat/v1/"); _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _settings.MinimaxApiKey); _httpClient.Timeout = TimeSpan.FromMinutes(2); } /// /// Bir sahnenin narration metnini sese çevirir ve dosyaya kaydeder. /// public async Task GenerateNarrationAsync( ScenePayload scene, string outputDirectory, string voiceStyle, CancellationToken ct) { _logger.LogInformation( "🎙️ Minimax TTS üretimi — Sahne {Order}: \"{Text}\"", scene.Order, scene.NarrationText[..Math.Min(60, scene.NarrationText.Length)]); // Varsayılan voiceStyle kullan veya fallback var voiceId = string.IsNullOrWhiteSpace(voiceStyle) ? _settings.MinimaxTtsVoiceId : voiceStyle; var requestBody = new { model = "speech-01-turbo", text = scene.NarrationText, voice_setting = new { voice_id = voiceId, speed = 1.0, vol = 1.0, pitch = 0 }, audio_setting = new { sample_rate = 32000, bitrate = 128000, format = "mp3", channel = 1 } }; var content = new StringContent( JsonSerializer.Serialize(requestBody), Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync("t2a_v2", content, ct); response.EnsureSuccessStatusCode(); // Minimax T2A V2 returns JSON with data.audio containing hex string var responseString = await response.Content.ReadAsStringAsync(ct); var jsonResponse = JsonSerializer.Deserialize(responseString); if (jsonResponse.TryGetProperty("data", out var dataElement) && dataElement.TryGetProperty("audio", out var audioHex)) { var hexString = audioHex.GetString() ?? ""; byte[] audioBytes = ConvertHexStringToByteArray(hexString); var outputPath = Path.Combine(outputDirectory, $"scene_{scene.Order:D2}_narration.mp3"); await File.WriteAllBytesAsync(outputPath, audioBytes, ct); var fileInfo = new FileInfo(outputPath); _logger.LogInformation( "Minimax TTS tamamlandı — Sahne {Order}: {Size} bytes", scene.Order, fileInfo.Length); return new GeneratedMediaFile { SceneId = scene.Id, SceneOrder = scene.Order, Type = MediaFileType.AudioNarration, LocalPath = outputPath, FileSizeBytes = fileInfo.Length, DurationSeconds = scene.Duration, MimeType = "audio/mpeg", AiProvider = "minimax" }; } else { throw new Exception("Minimax API response invalid: " + responseString); } } private static byte[] ConvertHexStringToByteArray(string hexString) { if (hexString.Length % 2 != 0) { throw new ArgumentException("Hex string must have an even length."); } byte[] data = new byte[hexString.Length / 2]; for (int index = 0; index < data.Length; index++) { string byteValue = hexString.Substring(index * 2, 2); data[index] = byte.Parse(byteValue, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture); } return data; } }