Update code to use common HttpClient
Get title and descrioption using groq api by passing proper prompt and store in google sheet
Use API key in the google text to speech generation


        public static async Task Main(string[] args)
        {
            try
            {
                var builder = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);

                IConfiguration configuration = builder.Build();

                googleSheetManager = new GoogleSheetManager(configuration);
                googleSheetManager.IntializeSheet("1ibVVSyj3k0Cp9_FLHJYRr70x2xuTfC7Anrp9_n-jqGc");

                // Fetch pending rows
                var pendingRows = GetPendingRowsFromGoogleSheet();
                var allReelPrompts = new List<ReelResponse>();

                foreach (var row in pendingRows)
                {
                    string subject = row[0].ToString();
                    string background = row[1].ToString();
                    string baseFolder = Path.Combine(Directory.GetCurrentDirectory(), subject);
                    Directory.CreateDirectory(baseFolder);
                    string imagesFolder = Path.Combine(baseFolder, "images");
                    string videosFolder = Path.Combine(baseFolder, "videos");

                    // Create directory structure
                    Directory.CreateDirectory(imagesFolder);
                    Directory.CreateDirectory(videosFolder);

                    var reelPrompts = await GenerateScript(subject, background);
                    allReelPrompts.AddRange(reelPrompts);

                    for (int i = 0; i < reelPrompts.Count; i++)
                    {
                        await GenerateImage(reelPrompts[i].imageprompt, background, imagesFolder, i);
                    }

                    await GenerateNarration(allReelPrompts);
                    await GenerateAndMergeMedia(allReelPrompts, imagesFolder, videosFolder);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
        }

        private static IList<IList<object>> GetPendingRowsFromGoogleSheet()
        {
            try
            {
                return googleSheetManager.GetEntries("Sheet1!A2:G").Where(row => row[6]?.ToString().ToLower() == "pending").ToList();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error fetching rows from Google Sheets: {ex.Message}");
                return new List<IList<object>>();
            }
        }

        private static async Task<List<ReelResponse>> GenerateScript(string subject, string background)
        {
            var ret = new List<ReelResponse>();
            try
            {
                string userPrompt = $@"You are tasked with creating an engaging YouTube Shorts video script, transforming the subject into a captivating story in JSON format, including detailed imagePrompt descriptions of characters and backgrounds; consistently describe any dog as 'red small pug'; Subject: {subject}; Story Background: {background}; Duration: 30 seconds; Tone: Storytelling; Minimum Scenes: 7-8; Response must be a JSON array of objects with properties (imagePrompt, narration, startTime, endTime); return only JSON, no text.";

                string jsonString = $@"
                {{
                    ""model"": ""llama-3.3-70b-versatile"",
                    ""messages"": [
                        {{
                            ""role"": ""user"",
                            ""content"": ""{userPrompt}""
                        }}
                    ],
                    ""max_tokens"": 7000,
                    ""stream"": false
                }}";
                var httpClient = new HttpClient();

                var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", groqKey);
                var result = await httpClient.PostAsync(scriptGenerationUrl, httpContent);

                result.EnsureSuccessStatusCode();
                var jsonResponse = await result.Content.ReadAsStringAsync();

                ChatCompletionResponse response = JsonConvert.DeserializeObject<ChatCompletionResponse>(jsonResponse);
                string jsonContent = response.Choices[0].Message.Content;

                ret = JsonConvert.DeserializeObject<List<ReelResponse>>(jsonContent);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error generating script for prompt '{subject}': {ex.Message}");
            }
            return ret;
        }

        private static async Task GenerateImage(string imagePrompt, string background, string imagesFolder, int i)
        {
            try
            {
                Console.WriteLine($"Generating image for prompt: {imagePrompt}");

                string requestBody = "{\"data\":[\"" + imagePrompt + ". Guidelines : Image should have background of " + background + ", Do not blur background, Avoid busy backgrounds; keep the focus on the main subject. Avoid dark and dull color palettes, All characters should look like real, Do not include exaggerated facial expressions, Do not include generic stock images or poses , Make it attractive and eye catchy. Width : 576px, Height : 1024px \",0,true,576,1024,3.5,28]}";

                var eventId = await ScheduleEvent(imageGenerationUrl, requestBody, hfKey);
                if (!string.IsNullOrEmpty(eventId))
                {
                    var result = await GetEventResponse(imageGenerationUrl, eventId, hfKey);
                    if (!string.IsNullOrEmpty(result))
                    {
                        await HandleGenerateImageResponse(result, imagesFolder, i);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error generating image for prompt '{imagePrompt}': {ex.Message}");
            }
        }

        private static async Task HandleGenerateImageResponse(string response, string imageFolder, int i)
        {
            try
            {
                var url = "";

                string pattern = @"event:\s*complete\s*data:\s*(\[(.*?)\])";
                Match match = Regex.Match(response, pattern, RegexOptions.Singleline);
                if (match.Success && match.Groups.Count > 1)
                {
                    string jsonData = match.Groups[1].Value;

                    // Parse the JSON response to extract URLs of images
                    using (JsonDocument doc = JsonDocument.Parse(jsonData))
                    {
                        JsonElement root = doc.RootElement;

                        foreach (JsonElement element in root.EnumerateArray())
                        {
                            if (element.TryGetProperty("url", out JsonElement urlElement))
                            {
                                url = urlElement.GetString();
                            }
                        }
                    }
                    if (!string.IsNullOrEmpty(url))
                    {
                        await DownloadImage(url.Replace("/g/", "/"), Path.Combine(imageFolder, $"{i}.webp"));
                    }
                }
                else
                {
                    Console.WriteLine("No complete event found.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error fetching image: {ex.Message}");
            }
        }

        private static async Task<string?> ScheduleEvent(string url, string requestBody, string? authorization = null)
        {
            try
            {
                if (!string.IsNullOrEmpty(authorization))
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorization);
                }
                var response = await client.PostAsync(url, new StringContent(requestBody, Encoding.UTF8, "application/json"));
                response.EnsureSuccessStatusCode();
                string jsonResponse = await response.Content.ReadAsStringAsync();

                using JsonDocument doc = JsonDocument.Parse(jsonResponse);
                string eventId = doc.RootElement.GetProperty("event_id").GetString();

                if (string.IsNullOrEmpty(eventId))
                {
                    Console.WriteLine("Error: Could not extract event_id.");
                    throw new Exception("Event creation Failed");
                }
                return eventId;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Event creation failed: {ex.Message}");
            }
            return null;
        }

        private static async Task<string> GetEventResponse(string url, string eventId, string? authorization = null)
        {
            try
            {
                var eventClient = new HttpClient
                {
                    Timeout = new TimeSpan(0, 5, 0)
                };
                var request = new HttpRequestMessage(HttpMethod.Get, url + "/" + eventId);
                if (!string.IsNullOrEmpty(authorization))
                {
                    eventClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorization);
                }
                var response = await eventClient.SendAsync(request);
                response.EnsureSuccessStatusCode();
                var result = await response.Content.ReadAsStringAsync();

                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed to download image: " + ex.Message);
            }
            return null;
        }

        private static async Task DownloadImage(string imageUrl, string savePath)
        {
            try
            {
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/webp"));
                byte[] imageBytes = await client.GetByteArrayAsync(imageUrl);
                await File.WriteAllBytesAsync(savePath, imageBytes);
                Console.WriteLine("Image downloaded successfully: " + savePath);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed to download image: " + ex.Message);
            }
        }

        private static async Task GenerateNarration(List<ReelResponse> reelPrompts,IConfiguration _configuration)
        {
            string[] Scopes = TextToSpeechClient.DefaultScopes.ToArray() ;
            var googleCredentialsJson = _configuration.GetSection("Google").GetChildren().Select(x => $"\"{x.Key}\":\"{x.Value}\"").Aggregate((x, y) => $"{x},{y}");
            GoogleCredential credential = GoogleCredential.FromJson($"{{ {googleCredentialsJson} }}")
                    .CreateScoped(Scopes);

            var speechClient = TextToSpeechClient.Create();
            foreach (var reelPrompt in reelPrompts)
            {
                var input = new SynthesisInput
                {
                    Text = reelPrompt.narration
                };
                var voiceSelection = new VoiceSelectionParams
                {
                    LanguageCode = "en-US",
                    SsmlGender = SsmlVoiceGender.Male
                };
                var audioConfig = new AudioConfig
                {
                    AudioEncoding = AudioEncoding.Mp3
                };

                var response = await speechClient.SynthesizeSpeechAsync(input, voiceSelection, audioConfig);
                using (var output = File.Create($"{reelPrompt.narration}.mp3"))
                {
                    response.AudioContent.WriteTo(output);
                    Console.WriteLine($"Generated narration for: {reelPrompt.narration}");
                }
            }
        }

        private static async Task GenerateAndMergeMedia(List<ReelResponse> reelPrompts, string imagesFolder, string videosFolder)
        {
            for (int i = 0; i < reelPrompts.Count; i++)
            {
                try
                {
                    string imageFilePath = Path.Combine(imagesFolder, $"{i}.webp");
                    string audioFilePath = $"{reelPrompts[i].narration}.mp3"; // Ensure this matches your saved audio
                    string outputFilePath = Path.Combine(videosFolder, $"output_{i}.mp4");

                    await MergeAudioAndImagesAsync(audioFilePath, imageFilePath, outputFilePath);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error merging media for {reelPrompts[i].narration}: {ex.Message}");
                }
            }
        }

        private static async Task MergeAudioAndImagesAsync(string audioFilePath, string imageFilePath, string outputFilePath)
        {
            var ffmpegCommand = $"-loop 1 -i \"{imageFilePath}\" -i \"{audioFilePath}\" -c:v libx264 -c:a aac -b:a 192k -shortest -vf \"scale=1280:720\" -y \"{outputFilePath}\"";

            try
            {
                using (var process = new System.Diagnostics.Process())
                {
                    process.StartInfo.FileName = "ffmpeg"; // Ensure ffmpeg is installed and in your PATH
                    process.StartInfo.Arguments = ffmpegCommand;
                    process.StartInfo.RedirectStandardOutput = true;
                    process.StartInfo.RedirectStandardError = true;
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.CreateNoWindow = true;

                    process.Start();
                    string output = await process.StandardOutput.ReadToEndAsync();
                    string error = await process.StandardError.ReadToEndAsync();

                    await process.WaitForExitAsync();

                    if (process.ExitCode == 0)
                    {
                        Console.WriteLine($"Successfully created video: {outputFilePath}");
                    }
                    else
                    {
                        Console.WriteLine($"FFmpeg Error: {error}");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error running FFmpeg: {ex.Message}");
            }
        }

        public class ReelResponse
        {
            public string imageprompt { get; set; }
            public string narration { get; set; }
            public string starttime { get; set; }
            public string endTime { get; set; }
        }

        public class ChatCompletionResponse
        {
            public string Object { get; set; }
            public string Id { get; set; }
            public long Created { get; set; }
            public string Model { get; set; }
            public List<Choice> Choices { get; set; }
        }

        public class Choice
        {
            public int Index { get; set; }
            public Message Message { get; set; }
            public string FinishReason { get; set; }
        }

        public class Message
        {
            public string Role { get; set; }
            public string Content { get; set; }
        }