Create Viral Videos with SORA in .NET MAUI

Generate Viral Videos with SORA and .NET MAUI

Hello devs! I'm so excited to be part of the .NET MAUI UI July – 2025 calendar! I invite you to explore all the fabulous posts that other authors have published on topics related to .NET MAUI.

In this post, I'm going to show you how I created an application that connects to the SORA service of Azure OpenAI for generating stunning videos. This project is just a part of a more complex system that I developed some time ago that allows for automated YouTube video generation. Personally, it's a topic that I'm passionate about, and I found it interesting to share it with you. I should add that I made extensive use of the VS Code agent to speed up the demo development. Let's get started!

General Overview of the Video Generator Using SORA

The video generation project contains 4 main pages that guide the user through the process of generating a video. These pages are as follows:

AddVideoIdeaPage.xaml: Page to enter the video prompt and configuration
SettingsPage.xaml: Page to add the keys for the Azure OpenAI services
VideoDisplayPage.xaml: Page that displays the generated video
VideoPromptsPage.xaml: Page that creates the enhanced prompt for video generation

Next, I'll show you each of the pages:

Creating the Settings Page

Let's start with the simplest page, which is the settings page for entering keys to connect the app with the Azure OpenAI services. In this case, the definition of the XAML page looks as follows:
				
					<ScrollView>
    <VerticalStackLayout Padding="20" Spacing="25">

        <!--  Page Header  -->
        <VerticalStackLayout Spacing="5">
            <Label
                FontAttributes="Bold"
                FontSize="28"
                HorizontalOptions="Center"
                Text="App Settings"
                TextColor="{AppThemeBinding Light={StaticResource Primary},
                                            Dark={StaticResource White}}" />

            <Label
                FontSize="14"
                HorizontalOptions="Center"
                Text="Configure your AI services connections"
                TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                            Dark={StaticResource Gray300}}" />
        </VerticalStackLayout>

        <!--  Azure LLM Configuration Section  -->
        <Border
            Padding="20"
            BackgroundColor="{AppThemeBinding Light={StaticResource White},
                                                Dark={StaticResource Gray900}}"
            Stroke="{AppThemeBinding Light={StaticResource Gray200},
                                        Dark={StaticResource Gray600}}"
            StrokeShape="RoundRectangle 12"
            StrokeThickness="1">

            <VerticalStackLayout Spacing="15">

                <!--  Section Header  -->
                <Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="10">
                    <Border
                        Grid.Column="0"
                        BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
                                                            Dark={StaticResource Primary}}"
                        HeightRequest="32"
                        StrokeShape="RoundRectangle 8"
                        VerticalOptions="Center"
                        WidthRequest="32">
                        <Label
                            FontSize="16"
                            HorizontalOptions="Center"
                            Text="🧠"
                            TextColor="White"
                            VerticalOptions="Center" />
                    </Border>

                    <VerticalStackLayout Grid.Column="1" VerticalOptions="Center">
                        <Label
                            FontAttributes="Bold"
                            FontSize="18"
                            Text="Azure LLM Service"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                        <Label
                            FontSize="12"
                            Text="Configure your Azure Language Model connection"
                            TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                        Dark={StaticResource Gray400}}" />
                    </VerticalStackLayout>

                    <Button
                        Grid.Column="2"
                        BackgroundColor="Transparent"
                        BorderColor="{AppThemeBinding Light={StaticResource Primary},
                                                        Dark={StaticResource White}}"
                        BorderWidth="1"
                        Command="{Binding TestAzureLlmConnectionCommand}"
                        CornerRadius="8"
                        FontSize="12"
                        HeightRequest="32"
                        IsEnabled="{Binding IsAzureLlmConfigComplete}"
                        Text="Test"
                        TextColor="{AppThemeBinding Light={StaticResource Primary},
                                                    Dark={StaticResource White}}"
                        WidthRequest="60" />
                </Grid>

                <!--  Endpoint Field  -->
                <VerticalStackLayout Spacing="5">
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="Endpoint URL"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />

                    <Border
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                    Dark={StaticResource Gray500}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Entry
                            FontSize="14"
                            Placeholder="https://your-resource.openai.azure.com/"
                            PlaceholderColor="{AppThemeBinding Light={StaticResource Gray400},
                                                                Dark={StaticResource Gray500}}"
                            Text="{Binding AzureLlmEndpoint}"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                    </Border>
                </VerticalStackLayout>

                <!--  API Key Field  -->
                <VerticalStackLayout Spacing="5">
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="API Key"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />

                    <Border
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                    Dark={StaticResource Gray500}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Grid ColumnDefinitions="*,Auto">
                            <Entry
                                Grid.Column="0"
                                FontSize="14"
                                IsPassword="{Binding IsAzureLlmApiKeyVisible, Converter={StaticResource InvertedBoolConverter}}"
                                Placeholder="Enter your Azure API key"
                                PlaceholderColor="{AppThemeBinding Light={StaticResource Gray400},
                                                                    Dark={StaticResource Gray500}}"
                                Text="{Binding AzureLlmApiKey}"
                                TextColor="{AppThemeBinding Light={StaticResource Black},
                                                            Dark={StaticResource White}}" />

                            <Button
                                Grid.Column="1"
                                BackgroundColor="Transparent"
                                Command="{Binding ToggleAzureLlmApiKeyVisibilityCommand}"
                                FontSize="16"
                                Padding="5"
                                Text="{Binding AzureLlmApiKeyToggleIcon}"
                                TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                            Dark={StaticResource Gray400}}" />
                        </Grid>
                    </Border>
                </VerticalStackLayout>

                <!--  Deployment Name Field  -->
                <VerticalStackLayout Spacing="5">
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="Deployment Name"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />

                    <Border
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                    Dark={StaticResource Gray500}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Entry
                            FontSize="14"
                            Placeholder="gpt-4, gpt-35-turbo, etc."
                            PlaceholderColor="{AppThemeBinding Light={StaticResource Gray400},
                                                                Dark={StaticResource Gray500}}"
                            Text="{Binding AzureLlmDeployment}"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                    </Border>
                </VerticalStackLayout>

            </VerticalStackLayout>
        </Border>

        <!--  SORA Configuration Section  -->
        <Border
            Padding="20"
            BackgroundColor="{AppThemeBinding Light={StaticResource White},
                                                Dark={StaticResource Gray900}}"
            Stroke="{AppThemeBinding Light={StaticResource Gray200},
                                        Dark={StaticResource Gray600}}"
            StrokeShape="RoundRectangle 12"
            StrokeThickness="1">

            <VerticalStackLayout Spacing="15">

                <!--  Section Header  -->
                <Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="10">
                    <Border
                        Grid.Column="0"
                        BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
                                                            Dark={StaticResource Primary}}"
                        HeightRequest="32"
                        StrokeShape="RoundRectangle 8"
                        VerticalOptions="Center"
                        WidthRequest="32">
                        <Label
                            FontSize="16"
                            HorizontalOptions="Center"
                            Text="🎬"
                            TextColor="White"
                            VerticalOptions="Center" />
                    </Border>

                    <VerticalStackLayout Grid.Column="1" VerticalOptions="Center">
                        <Label
                            FontAttributes="Bold"
                            FontSize="18"
                            Text="SORA Video Service"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                        <Label
                            FontSize="12"
                            Text="Configure your SORA video generation connection"
                            TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                        Dark={StaticResource Gray400}}" />
                    </VerticalStackLayout>

                    <Button
                        Grid.Column="2"
                        BackgroundColor="Transparent"
                        BorderColor="{AppThemeBinding Light={StaticResource Primary},
                                                        Dark={StaticResource White}}"
                        BorderWidth="1"
                        Command="{Binding TestSoraConnectionCommand}"
                        CornerRadius="8"
                        FontSize="12"
                        HeightRequest="32"
                        IsEnabled="{Binding IsSoraConfigComplete}"
                        Text="Test"
                        TextColor="{AppThemeBinding Light={StaticResource Primary},
                                                    Dark={StaticResource White}}"
                        WidthRequest="60" />
                </Grid>

                <!--  Endpoint Field  -->
                <VerticalStackLayout Spacing="5">
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="Endpoint URL"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />

                    <Border
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                    Dark={StaticResource Gray500}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Entry
                            FontSize="14"
                            Placeholder="https://api.openai.com/v1/sora/"
                            PlaceholderColor="{AppThemeBinding Light={StaticResource Gray400},
                                                                Dark={StaticResource Gray500}}"
                            Text="{Binding SoraEndpoint}"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                    </Border>
                </VerticalStackLayout>

                <!--  API Key Field  -->
                <VerticalStackLayout Spacing="5">
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="API Key"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />

                    <Border
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                    Dark={StaticResource Gray500}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Grid ColumnDefinitions="*,Auto">
                            <Entry
                                Grid.Column="0"
                                FontSize="14"
                                IsPassword="{Binding IsSoraApiKeyVisible, Converter={StaticResource InvertedBoolConverter}}"
                                Placeholder="Enter your SORA API key"
                                PlaceholderColor="{AppThemeBinding Light={StaticResource Gray400},
                                                                    Dark={StaticResource Gray500}}"
                                Text="{Binding SoraApiKey}"
                                TextColor="{AppThemeBinding Light={StaticResource Black},
                                                            Dark={StaticResource White}}" />

                            <Button
                                Grid.Column="1"
                                BackgroundColor="Transparent"
                                Command="{Binding ToggleSoraApiKeyVisibilityCommand}"
                                FontSize="16"
                                Padding="5"
                                Text="{Binding SoraApiKeyToggleIcon}"
                                TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                            Dark={StaticResource Gray400}}" />
                        </Grid>
                    </Border>
                </VerticalStackLayout>

                <!--  Deployment Name Field  -->
                <VerticalStackLayout Spacing="5">
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="Model Name"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />

                    <Border
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                    Dark={StaticResource Gray500}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Entry
                            FontSize="14"
                            Placeholder="sora-1.0, sora-turbo, etc."
                            PlaceholderColor="{AppThemeBinding Light={StaticResource Gray400},
                                                                Dark={StaticResource Gray500}}"
                            Text="{Binding SoraDeployment}"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                    </Border>
                </VerticalStackLayout>

            </VerticalStackLayout>
        </Border>

        <!--  Action Buttons  -->
        <Grid ColumnDefinitions="*,*" ColumnSpacing="15" Margin="0,10,0,0">

            <Button
                Grid.Column="0"
                BackgroundColor="Transparent"
                BorderColor="#DC3545"
                BorderWidth="2"
                Command="{Binding ClearSettingsCommand}"
                CornerRadius="25"
                FontAttributes="Bold"
                FontSize="16"
                HeightRequest="50"
                Text="Clear All"
                TextColor="#DC3545" />

            <Button
                Grid.Column="1"
                BackgroundColor="{StaticResource Primary}"
                Command="{Binding SaveSettingsCommand}"
                CornerRadius="25"
                FontAttributes="Bold"
                FontSize="16"
                HeightRequest="50"
                IsEnabled="{Binding IsSaving, Converter={StaticResource InvertedBoolConverter}}"
                Text="Save Settings"
                TextColor="White" />

        </Grid>

        <!--  Loading Indicator  -->
        <VerticalStackLayout IsVisible="{Binding IsSaving}" 
                        Padding="20"
                        HorizontalOptions="Center" 
                        VerticalOptions="Center"
                        Spacing="15">
            
            <ActivityIndicator IsRunning="{Binding IsSaving}"
                                Color="{StaticResource Primary}"
                                HeightRequest="40"
                                WidthRequest="40"
                                HorizontalOptions="Center" />
            
            <Label Text="Saving Configuration..." 
                    FontSize="16" 
                    FontAttributes="Bold" 
                    HorizontalOptions="Center" 
                    TextColor="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Primary}}" />
            
            <Label Text="Validating and storing your settings" 
                    FontSize="14" 
                    HorizontalOptions="Center" 
                    TextColor="{AppThemeBinding Light={StaticResource Gray600}, Dark={StaticResource Gray400}}" />
            
        </VerticalStackLayout>

    </VerticalStackLayout>
</ScrollView>
				
			
This page is linked to a ViewModel called SettingsViewModel, which looks like this:
				
					public partial class SettingsViewModel : ObservableObject
{
    // Constants for Preferences keys
    private const string AZURE_LLM_ENDPOINT_KEY = "AzureLlmEndpoint";
    private const string AZURE_LLM_API_KEY = "AzureLlmApiKey";
    private const string AZURE_LLM_DEPLOYMENT_KEY = "AzureLlmDeployment";
    private const string SORA_ENDPOINT_KEY = "SoraEndpoint";
    private const string SORA_API_KEY = "SoraApiKey";
    private const string SORA_DEPLOYMENT_KEY = "SoraDeployment";

    [ObservableProperty]
    private string azureLlmEndpoint = string.Empty;

    [ObservableProperty]
    private string azureLlmApiKey = string.Empty;

    [ObservableProperty]
    private string azureLlmDeployment = string.Empty;

    [ObservableProperty]
    private string soraEndpoint = string.Empty;

    [ObservableProperty]
    private string soraApiKey = string.Empty;

    [ObservableProperty]
    private string soraDeployment = string.Empty;

    [ObservableProperty]
    private bool isAzureLlmApiKeyVisible = false;

    [ObservableProperty]
    private bool isSoraApiKeyVisible = false;

    [ObservableProperty]
    private string azureLlmApiKeyToggleIcon = "👁️";

    [ObservableProperty]
    private string soraApiKeyToggleIcon = "👁️";

    [ObservableProperty]
    private bool isSaving = false;

    // Reference to the page for displaying alerts
    private Page? _page;

    public SettingsViewModel()
    {
        LoadSettings();
    }

    public void SetPage(Page page)
    {
        _page = page;
    }

    /// <summary>
    /// Loads saved settings from Preferences
    /// </summary>
    public void LoadSettings()
    {
        AzureLlmEndpoint = Preferences.Default.Get(AZURE_LLM_ENDPOINT_KEY, string.Empty);
        AzureLlmApiKey = Preferences.Default.Get(AZURE_LLM_API_KEY, string.Empty);
        AzureLlmDeployment = Preferences.Default.Get(AZURE_LLM_DEPLOYMENT_KEY, string.Empty);
        
        SoraEndpoint = Preferences.Default.Get(SORA_ENDPOINT_KEY, string.Empty);
        SoraApiKey = Preferences.Default.Get(SORA_API_KEY, string.Empty);
        SoraDeployment = Preferences.Default.Get(SORA_DEPLOYMENT_KEY, string.Empty);
    }

    /// <summary>
    /// Command to toggle visibility of Azure LLM API key
    /// </summary>
    [RelayCommand]
    private void ToggleAzureLlmApiKeyVisibility()
    {
        IsAzureLlmApiKeyVisible = !IsAzureLlmApiKeyVisible;
        AzureLlmApiKeyToggleIcon = IsAzureLlmApiKeyVisible ? "🙈" : "👁️";
    }

    /// <summary>
    /// Command to toggle visibility of SORA API key
    /// </summary>
    [RelayCommand]
    private void ToggleSoraApiKeyVisibility()
    {
        IsSoraApiKeyVisible = !IsSoraApiKeyVisible;
        SoraApiKeyToggleIcon = IsSoraApiKeyVisible ? "🙈" : "👁️";
    }

    /// <summary>
    /// Command to save all settings
    /// </summary>
    [RelayCommand]
    private async Task SaveSettings()
    {
        IsSaving = true;

        try
        {
            // Validate settings before saving
            if (!ValidateSettings())
            {
                return;
            }

            // Save Azure LLM settings
            Preferences.Default.Set(AZURE_LLM_ENDPOINT_KEY, AzureLlmEndpoint.Trim());
            Preferences.Default.Set(AZURE_LLM_API_KEY, AzureLlmApiKey.Trim());
            Preferences.Default.Set(AZURE_LLM_DEPLOYMENT_KEY, AzureLlmDeployment.Trim());

            // Save SORA settings
            Preferences.Default.Set(SORA_ENDPOINT_KEY, SoraEndpoint.Trim());
            Preferences.Default.Set(SORA_API_KEY, SoraApiKey.Trim());
            Preferences.Default.Set(SORA_DEPLOYMENT_KEY, SoraDeployment.Trim());

            if (_page != null)
            {
                await _page.DisplayAlert("Settings Saved", "All settings have been saved successfully!", "OK");
            }
        }
        catch (Exception ex)
        {
            if (_page != null)
            {
                await _page.DisplayAlert("Error", $"Failed to save settings: {ex.Message}", "OK");
            }
        }
        finally
        {
            IsSaving = false;
        }
    }

    /// <summary>
    /// Command to clear all settings
    /// </summary>
    [RelayCommand]
    private async Task ClearSettings()
    {
        if (_page != null)
        {
            bool confirmed = await _page.DisplayAlert(
                "Clear Settings",
                "Are you sure you want to clear all settings? This action cannot be undone.",
                "Clear",
                "Cancel");

            if (!confirmed)
                return;
        }

        try
        {
            // Clear from Preferences
            Preferences.Default.Remove(AZURE_LLM_ENDPOINT_KEY);
            Preferences.Default.Remove(AZURE_LLM_API_KEY);
            Preferences.Default.Remove(AZURE_LLM_DEPLOYMENT_KEY);
            Preferences.Default.Remove(SORA_ENDPOINT_KEY);
            Preferences.Default.Remove(SORA_API_KEY);
            Preferences.Default.Remove(SORA_DEPLOYMENT_KEY);

            // Clear from UI
            AzureLlmEndpoint = string.Empty;
            AzureLlmApiKey = string.Empty;
            AzureLlmDeployment = string.Empty;
            SoraEndpoint = string.Empty;
            SoraApiKey = string.Empty;
            SoraDeployment = string.Empty;

            if (_page != null)
            {
                await _page.DisplayAlert("Settings Cleared", "All settings have been cleared successfully!", "OK");
            }
        }
        catch (Exception ex)
        {
            if (_page != null)
            {
                await _page.DisplayAlert("Error", $"Failed to clear settings: {ex.Message}", "OK");
            }
        }
    }

    /// <summary>
    /// Command to test Azure LLM connection
    /// </summary>
    [RelayCommand]
    private async Task TestAzureLlmConnection()
    {
        if (string.IsNullOrWhiteSpace(AzureLlmEndpoint) || 
            string.IsNullOrWhiteSpace(AzureLlmApiKey) || 
            string.IsNullOrWhiteSpace(AzureLlmDeployment))
        {
            if (_page != null)
            {
                await _page.DisplayAlert("Incomplete Configuration", 
                    "Please fill in all Azure LLM fields before testing.", "OK");
            }
            return;
        }

        // TODO: Implement actual connection test
        if (_page != null)
        {
            await _page.DisplayAlert("Test Connection", 
                "Azure LLM connection test functionality will be implemented soon!", "OK");
        }
    }

    /// <summary>
    /// Command to test SORA connection
    /// </summary>
    [RelayCommand]
    private async Task TestSoraConnection()
    {
        if (string.IsNullOrWhiteSpace(SoraEndpoint) || 
            string.IsNullOrWhiteSpace(SoraApiKey) || 
            string.IsNullOrWhiteSpace(SoraDeployment))
        {
            if (_page != null)
            {
                await _page.DisplayAlert("Incomplete Configuration", 
                    "Please fill in all SORA fields before testing.", "OK");
            }
            return;
        }

        // TODO: Implement actual connection test
        if (_page != null)
        {
            await _page.DisplayAlert("Test Connection", 
                "SORA connection test functionality will be implemented soon!", "OK");
        }
    }

    /// <summary>
    /// Validates all settings before saving
    /// </summary>
    private bool ValidateSettings()
    {
        var errors = new List<string>();

        // Validate Azure LLM settings
        if (string.IsNullOrWhiteSpace(AzureLlmEndpoint))
            errors.Add("Azure LLM Endpoint is required");
        else if (!Uri.TryCreate(AzureLlmEndpoint, UriKind.Absolute, out _))
            errors.Add("Azure LLM Endpoint must be a valid URL");

        if (string.IsNullOrWhiteSpace(AzureLlmApiKey))
            errors.Add("Azure LLM API Key is required");

        if (string.IsNullOrWhiteSpace(AzureLlmDeployment))
            errors.Add("Azure LLM Deployment name is required");

        // Validate SORA settings
        if (string.IsNullOrWhiteSpace(SoraEndpoint))
            errors.Add("SORA Endpoint is required");
        else if (!Uri.TryCreate(SoraEndpoint, UriKind.Absolute, out _))
            errors.Add("SORA Endpoint must be a valid URL");

        if (string.IsNullOrWhiteSpace(SoraApiKey))
            errors.Add("SORA API Key is required");

        if (string.IsNullOrWhiteSpace(SoraDeployment))
            errors.Add("SORA Deployment name is required");

        if (errors.Count > 0 && _page != null)
        {
            Task.Run(async () => await _page.DisplayAlert("Validation Error", 
                string.Join("\n", errors), "OK"));
            return false;
        }

        return true;
    }

    /// <summary>
    /// Checks if all required settings are configured
    /// </summary>
    public bool AreSettingsComplete =>
        !string.IsNullOrWhiteSpace(AzureLlmEndpoint) &&
        !string.IsNullOrWhiteSpace(AzureLlmApiKey) &&
        !string.IsNullOrWhiteSpace(AzureLlmDeployment) &&
        !string.IsNullOrWhiteSpace(SoraEndpoint) &&
        !string.IsNullOrWhiteSpace(SoraApiKey) &&
        !string.IsNullOrWhiteSpace(SoraDeployment);

    /// <summary>
    /// Checks if Azure LLM settings are complete
    /// </summary>
    public bool IsAzureLlmConfigComplete =>
        !string.IsNullOrWhiteSpace(AzureLlmEndpoint) &&
        !string.IsNullOrWhiteSpace(AzureLlmApiKey) &&
        !string.IsNullOrWhiteSpace(AzureLlmDeployment);

    /// <summary>
    /// Checks if SORA settings are complete
    /// </summary>
    public bool IsSoraConfigComplete =>
        !string.IsNullOrWhiteSpace(SoraEndpoint) &&
        !string.IsNullOrWhiteSpace(SoraApiKey) &&
        !string.IsNullOrWhiteSpace(SoraDeployment);
}
				
			
When using MVVM Toolkit, the task has been greatly simplified. Similarly, you can see that I make use of the Preferences class to store these keys and read them in case they exist previously.

The result of this page can be seen below:
The settings page in action

Creating the parameters page for video generation

The second page created is called AddVideoIdeaPage.xaml. This page is designed for the user to enter their viral video idea to be generated:
The prompt box for users to describe the video they want to create
Something that is very common is for users to enter simple prompts that do not describe well what the video generator should produce. To address this issue, I added a checkbox that allows the user to decide whether they want to enhance the prompt using AI, which will invoke the Chat service to improve the entered prompt:
Checkbox to enable AI-powered prompt enhancement
Furthermore, it is on this same page where the user can configure the output video, as SORA allows different configuration options for creating videos in horizontal or vertical formats, different resolutions, and even durations of the generated videos:
Settings for the video output generated by SORA
The code for this page is a bit more complex:
				
					<ScrollView>
    <VerticalStackLayout
        Padding="30,0"
        Spacing="25"
        VerticalOptions="Center">

        <!--  Title  -->
        <Label
            FontAttributes="Bold"
            FontSize="24"
            HorizontalOptions="Center"
            Text="Share Your Viral Video Idea"
            TextColor="{AppThemeBinding Light={StaticResource Primary},
                                        Dark={StaticResource White}}" />

        <!--  Subtitle  -->
        <Label
            FontSize="16"
            HorizontalOptions="Center"
            Text="Describe your creative idea for a viral video"
            TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                        Dark={StaticResource Gray300}}" />

        <!--  Text Input Area  -->
        <Border
            Padding="10"
            BackgroundColor="{AppThemeBinding Light={StaticResource White},
                                              Dark={StaticResource Gray950}}"
            Stroke="{AppThemeBinding Light={StaticResource Gray200},
                                     Dark={StaticResource Gray500}}"
            StrokeShape="RoundRectangle 8"
            StrokeThickness="1">

            <Editor
                BackgroundColor="Transparent"
                FontSize="16"
                HeightRequest="200"
                Placeholder="Tell us about your viral video idea... What makes it special? What's the concept, story, or hook that will capture viewers' attention?"
                PlaceholderColor="{AppThemeBinding Light={StaticResource Gray400},
                                                   Dark={StaticResource Gray500}}"
                Text="{Binding VideoIdea}"
                TextColor="{AppThemeBinding Light={StaticResource Black},
                                            Dark={StaticResource White}}" />

        </Border>

        <!--  Character count label  -->
        <Label
            FontSize="12"
            HorizontalOptions="End"
            Text="{Binding CharacterCountText}"
            TextColor="{AppThemeBinding Light={StaticResource Gray500},
                                        Dark={StaticResource Gray400}}" />

        <!--  AI Enhancement Option  -->
        <Border
            Margin="0,10,0,0"
            Padding="15"
            BackgroundColor="{AppThemeBinding Light={StaticResource Gray100},
                                              Dark={StaticResource Gray900}}"
            Stroke="{AppThemeBinding Light={StaticResource Gray200},
                                     Dark={StaticResource Gray500}}"
            StrokeShape="RoundRectangle 12"
            StrokeThickness="1">

            <Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="12">

                <!--  AI Enhancement Icon  -->
                <Border
                    Grid.Column="0"
                    BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
                                                      Dark={StaticResource Primary}}"
                    HeightRequest="32"
                    HorizontalOptions="Center"
                    StrokeShape="RoundRectangle 8"
                    VerticalOptions="Center"
                    WidthRequest="32">

                    <Label
                        FontSize="16"
                        HorizontalOptions="Center"
                        Text="✨"
                        TextColor="White"
                        VerticalOptions="Center" />
                </Border>

                <!--  Enhancement Description  -->
                <StackLayout
                    Grid.Column="1"
                    Spacing="2"
                    VerticalOptions="Center">
                    <Label
                        FontAttributes="Bold"
                        FontSize="16"
                        Text="AI Prompt Enhancement"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />

                    <Label
                        FontSize="13"
                        Text="Let AI optimize your idea for better viral potential"
                        TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                    Dark={StaticResource Gray400}}" />
                </StackLayout>

                <!--  Enhancement Toggle  -->
                <Border
                    Grid.Column="2"
                    BackgroundColor="{Binding EnhancementToggleBorderColor}"
                    HeightRequest="30"
                    StrokeShape="RoundRectangle 20"
                    VerticalOptions="Center"
                    WidthRequest="50">

                    <Border.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding ToggleEnhancementCommand}" />
                    </Border.GestureRecognizers>

                    <Grid>
                        <!--  Toggle Circle  -->
                        <Border
                            Margin="2,0"
                            BackgroundColor="White"
                            HeightRequest="26"
                            HorizontalOptions="Start"
                            StrokeShape="RoundRectangle 14"
                            TranslationX="{Binding EnhancementToggleTranslationX}"
                            VerticalOptions="Center"
                            WidthRequest="26">

                            <Label
                                FontSize="12"
                                HorizontalOptions="Center"
                                Opacity="{Binding EnhancementToggleOpacity}"
                                Text="{Binding EnhancementToggleIcon}"
                                VerticalOptions="Center" />
                        </Border>
                    </Grid>
                </Border>

            </Grid>
        </Border>

        <!--  Video Configuration Section  -->
        <Border
            Margin="0,20,0,0"
            Padding="20"
            BackgroundColor="{AppThemeBinding Light={StaticResource Gray100},
                                              Dark={StaticResource Gray900}}"
            Stroke="{AppThemeBinding Light={StaticResource Gray200},
                                     Dark={StaticResource Gray500}}"
            StrokeShape="RoundRectangle 12"
            StrokeThickness="1">

            <StackLayout Spacing="20">

                <!--  Configuration Header  -->
                <StackLayout Orientation="Horizontal" Spacing="10">
                    <Border
                        BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
                                                          Dark={StaticResource Primary}}"
                        HeightRequest="28"
                        StrokeShape="RoundRectangle 6"
                        VerticalOptions="Center"
                        WidthRequest="28">
                        <Label
                            FontSize="14"
                            HorizontalOptions="Center"
                            Text="🎬"
                            TextColor="White"
                            VerticalOptions="Center" />
                    </Border>

                    <Label
                        FontAttributes="Bold"
                        FontSize="18"
                        Text="Video Configuration"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}"
                        VerticalOptions="Center" />
                </StackLayout>

                <!--  Resolution Selection  -->
                <StackLayout>
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="Resolution"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />
                    <Label
                        FontSize="12"
                        Text="Select video quality level - higher resolution provides better quality but takes longer to generate"
                        TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                    Dark={StaticResource Gray400}}" />

                    <Border
                        Margin="0,8,0,0"
                        BackgroundColor="{AppThemeBinding Light={StaticResource White},
                                                          Dark={StaticResource Gray950}}"
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                 Dark={StaticResource Gray600}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Picker
                            Title="Select Resolution"
                            BackgroundColor="Transparent"
                            FontSize="16"
                            ItemsSource="{Binding ResolutionOptions}"
                            SelectedItem="{Binding SelectedResolution}"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                    </Border>
                </StackLayout>

                <!--  Duration Selection  -->
                <StackLayout>
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="Duration (seconds)"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />
                    <Label
                        FontSize="12"
                        Text="Longer videos take more time to generate"
                        TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                    Dark={StaticResource Gray400}}" />

                    <Border
                        Margin="0,8,0,0"
                        BackgroundColor="{AppThemeBinding Light={StaticResource White},
                                                          Dark={StaticResource Gray950}}"
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                 Dark={StaticResource Gray600}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Picker
                            Title="Select Duration"
                            BackgroundColor="Transparent"
                            FontSize="16"
                            ItemsSource="{Binding DurationOptions}"
                            SelectedItem="{Binding SelectedDuration}"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                    </Border>
                </StackLayout>

                <!--  Orientation Selection  -->
                <StackLayout>
                    <Label
                        FontAttributes="Bold"
                        FontSize="14"
                        Text="Orientation"
                        TextColor="{AppThemeBinding Light={StaticResource Black},
                                                    Dark={StaticResource White}}" />
                    <Label
                        FontSize="12"
                        Text="Choose aspect ratio: Horizontal (16:9), Vertical (9:16), or Square (1:1)"
                        TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                    Dark={StaticResource Gray400}}" />

                    <Border
                        Margin="0,8,0,0"
                        BackgroundColor="{AppThemeBinding Light={StaticResource White},
                                                          Dark={StaticResource Gray950}}"
                        Stroke="{AppThemeBinding Light={StaticResource Gray300},
                                                 Dark={StaticResource Gray600}}"
                        StrokeShape="RoundRectangle 8"
                        StrokeThickness="1">
                        <Picker
                            Title="Select Orientation"
                            BackgroundColor="Transparent"
                            FontSize="16"
                            ItemsSource="{Binding OrientationOptions}"
                            SelectedItem="{Binding SelectedOrientation}"
                            TextColor="{AppThemeBinding Light={StaticResource Black},
                                                        Dark={StaticResource White}}" />
                    </Border>
                </StackLayout>

            </StackLayout>
        </Border>

        <!--  Generate Button  -->
        <Button
            Margin="0,20,0,0"
            BackgroundColor="{StaticResource Primary}"
            Command="{Binding GenerateViralStoryCommand}"
            CornerRadius="25"
            FontAttributes="Bold"
            FontSize="18"
            HeightRequest="50"
            HorizontalOptions="Fill"
            IsEnabled="{Binding CanGenerateStory}"
            Text="{Binding GenerateButtonText}"
            TextColor="White" />

        <!--  Enhanced Loading Indicator  -->
        <StackLayout
            Margin="0,20,0,0"
            HorizontalOptions="Center"
            IsVisible="{Binding IsGenerating}"
            Spacing="20"
            VerticalOptions="Center">

            <ActivityIndicator
                HeightRequest="50"
                HorizontalOptions="Center"
                IsRunning="{Binding IsGenerating}"
                WidthRequest="50"
                Color="{StaticResource Primary}" />

            <StackLayout Spacing="10">
                <Label
                    FontAttributes="Bold"
                    FontSize="18"
                    HorizontalOptions="Center"
                    HorizontalTextAlignment="Center"
                    Text="🧠 Processing your idea..."
                    TextColor="{AppThemeBinding Light={StaticResource Primary},
                                                Dark={StaticResource Primary}}" />

                <Label
                    FontSize="14"
                    HorizontalOptions="Center"
                    HorizontalTextAlignment="Center"
                    LineBreakMode="WordWrap"
                    Text="We’re enhancing your idea with AI and generating optimized prompts to create an amazing viral video."
                    TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                                Dark={StaticResource Gray400}}" />

                <Label
                    FontAttributes="Italic"
                    FontSize="12"
                    HorizontalOptions="Center"
                    HorizontalTextAlignment="Center"
                    Text="⏱️ This process may take a few moments"
                    TextColor="{AppThemeBinding Light={StaticResource Gray500},
                                                Dark={StaticResource Gray500}}" />
            </StackLayout>

        </StackLayout>


    </VerticalStackLayout>
</ScrollView>
				
			
On the other hand, the ViewModel called AddVideoIdeaViewModel looks as follows:
				
					public partial class AddVideoIdeaViewModel : ObservableObject
{
    private readonly IChatService _chatService;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(CanGenerateStory))]
    private string videoIdea = string.Empty;

    [ObservableProperty]
    private string characterCountText = "0 characters";

    [ObservableProperty]
    private bool isEnhancementEnabled = false;

    [ObservableProperty]
    private string generateButtonText = "Generate Viral Story";

    [ObservableProperty]
    private string enhancementToggleIcon = "🤖";

    [ObservableProperty]
    private double enhancementToggleOpacity = 0.5;

    [ObservableProperty]
    private double enhancementToggleTranslationX = 0.0;

    [ObservableProperty]
    private Color enhancementToggleBorderColor = Colors.Gray;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(CanGenerateStory))]
    private bool isGenerating = false;

    // Video Configuration Properties
    [ObservableProperty]
    private string selectedResolution = "720";

    [ObservableProperty]
    private string selectedDuration = "10";

    [ObservableProperty]
    private string selectedOrientation = "Horizontal";

    // Available options for pickers
    public List<string> ResolutionOptions { get; } = ["480", "720", "1080"];
    public List<string> DurationOptions { get; } = ["5", "10", "15", "20"];
    public List<string> OrientationOptions { get; } = ["Horizontal", "Vertical", "Square"];

    // Reference to the page for displaying alerts
    private Page? _page;

    public AddVideoIdeaViewModel(IChatService chatService)
    {
        _chatService = chatService;
        CheckChatServiceConfiguration();
    }

    public void SetPage(Page page)
    {
        _page = page;
    }

    /// <summary>
    /// Checks if chat service is configured and updates UI accordingly
    /// </summary>
    private async void CheckChatServiceConfiguration()
    {
        try
        {
            var isConfigured = await _chatService.IsConfiguredAsync();
            if (!isConfigured)
            {
                // Update UI to indicate that enhancement requires configuration
                EnhancementToggleIcon = "⚙️";
            }
        }
        catch
        {
            // If checking fails, assume not configured
            EnhancementToggleIcon = "⚙️";
        }
    }

    /// <summary>
    /// Updates character count when video idea text changes
    /// </summary>
    partial void OnVideoIdeaChanged(string value)
    {
        var characterCount = string.IsNullOrEmpty(value) ? 0 : value.Length;
        CharacterCountText = $"{characterCount} characters";
    }

    /// <summary>
    /// Updates UI elements when enhancement toggle state changes
    /// </summary>
    partial void OnIsEnhancementEnabledChanged(bool value)
    {
        UpdateGenerateButtonText();
        UpdateToggleAppearance();
    }

    /// <summary>
    /// Command to toggle AI enhancement feature
    /// </summary>
    [RelayCommand]
    private async Task ToggleEnhancement()
    {
        IsEnhancementEnabled = !IsEnhancementEnabled;
        await AnimateToggle();
    }

    /// <summary>
    /// Command to generate viral story from user's idea
    /// </summary>
    [RelayCommand]
    private async Task GenerateViralStory()
    {
        if (string.IsNullOrWhiteSpace(VideoIdea))
        {
            if (_page != null)
            {
                await _page.DisplayAlert("Empty Idea", "Please enter your video idea before generating a viral story.", "OK");
            }
            return;
        }

        IsGenerating = true;

        try
        {
            string finalVideoIdea = VideoIdea;

            // Check if enhancement is enabled and service is configured
            if (IsEnhancementEnabled)
            {
                var isConfigured = await _chatService.IsConfiguredAsync();
                if (!isConfigured)
                {
                    if (_page != null)
                    {
                        var result = await _page.DisplayAlert(
                            "Service Not Configured", 
                            "AI enhancement requires Azure LLM configuration. Would you like to go to Settings to configure it?", 
                            "Go to Settings", 
                            "Continue without Enhancement");
                        
                        if (result)
                        {
                            await Shell.Current.GoToAsync("//Settings");
                            return;
                        }
                        else
                        {
                            // Continue without enhancement
                            IsEnhancementEnabled = false;
                            await AnimateToggle();
                        }
                    }
                }
                else
                {
                    // Enhance the idea using Azure LLM
                    try
                    {
                        finalVideoIdea = await _chatService.EnhancePromptAsync(VideoIdea);
                        
                        // No DisplayAlert - keep loading while processing
                        Debug.WriteLine($"[AddVideoIdea] Idea enhanced successfully: {finalVideoIdea}");
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"[AddVideoIdea] Enhancement failed: {ex.Message}");
                        if (_page != null)
                        {
                            await _page.DisplayAlert("Enhancement Failed", 
                                $"Failed to enhance the idea: {ex.Message}\n\nContinuing with original idea.", 
                                "OK");
                        }
                    }
                }
            }

            // TODO: Store the final video idea and enhancement status for the VideoPromptsPage
            // Pass the data through navigation parameters including video configuration
            var (width, height) = GetVideoDimensions();
            var duration = int.Parse(SelectedDuration);
            
            var navigationParameters = new Dictionary<string, object>
            {
                ["VideoIdea"] = VideoIdea,
                ["EnhancedIdea"] = finalVideoIdea ?? string.Empty,
                ["WasEnhanced"] = (finalVideoIdea != VideoIdea && !string.IsNullOrEmpty(finalVideoIdea)).ToString(),
                ["VideoWidth"] = width.ToString(),
                ["VideoHeight"] = height.ToString(),
                ["VideoDuration"] = duration.ToString(),
                ["VideoOrientation"] = SelectedOrientation
            };

            // Navigate to VideoPromptsPage after processing
            await Shell.Current.GoToAsync("//VideoPrompts", navigationParameters);
        }
        finally
        {
            IsGenerating = false;
        }
    }

    /// <summary>
    /// Updates the generate button text based on enhancement state
    /// </summary>
    private void UpdateGenerateButtonText()
    {
        GenerateButtonText = IsEnhancementEnabled 
            ? "✨ Enhance & Generate" 
            : "Generate Viral Story";
    }

    /// <summary>
    /// Updates toggle visual appearance based on current state
    /// </summary>
    private void UpdateToggleAppearance()
    {
        if (IsEnhancementEnabled)
        {
            EnhancementToggleIcon = "✨";
            EnhancementToggleOpacity = 1.0;
            EnhancementToggleTranslationX = 20.0;
            EnhancementToggleBorderColor = Color.FromRgb(0x00, 0xFF, 0x33); // Green
        }
        else
        {
            EnhancementToggleIcon = "🤖";
            EnhancementToggleOpacity = 0.5;
            EnhancementToggleTranslationX = 0.0;
            EnhancementToggleBorderColor = Colors.Gray;
        }
    }

    /// <summary>
    /// Animates the enhancement toggle switch
    /// </summary>
    private async Task AnimateToggle()
    {
        var animation = new Animation();
        var targetTranslation = IsEnhancementEnabled ? 20.0 : 0.0;
        var startTranslation = IsEnhancementEnabled ? 0.0 : 20.0;

        // Translation animation
        animation.Add(0, 1, new Animation(v => 
        {
            EnhancementToggleTranslationX = v;
        }, startTranslation, targetTranslation));

        // Color animation
        animation.Add(0, 1, new Animation(v => 
        {
            if (IsEnhancementEnabled)
            {
                var color = Color.FromRgba(
                    (int)(0x66 + (0x00 - 0x66) * v), // Red component
                    (int)(0x66 + (0xFF - 0x66) * v), // Green component  
                    (int)(0x66 + (0x33 - 0x66) * v), // Blue component
                    255);
                EnhancementToggleBorderColor = color;
            }
            else
            {
                var grayValue = (int)(0x66 + (0xCC - 0x66) * v);
                EnhancementToggleBorderColor = Color.FromRgb(grayValue, grayValue, grayValue);
            }
        }, 0, 1));

        var completionSource = new TaskCompletionSource<bool>();

        if (_page != null)
        {
            animation.Commit(_page, "ToggleAnimation", 16, 250, 
                Easing.CubicOut, (v, c) => completionSource.SetResult(true));
        }
        else
        {
            completionSource.SetResult(true);
        }

        await completionSource.Task;
    }

    /// <summary>
    /// Validates if the current idea is ready for generation
    /// </summary>
    public bool CanGenerateStory => !string.IsNullOrWhiteSpace(VideoIdea) && !IsGenerating;

    /// <summary>
    /// Gets video dimensions based on selected resolution and orientation
    /// Using SORA-supported resolution combinations
    /// </summary>
    /// <returns>Tuple with width and height values</returns>
    private (int width, int height) GetVideoDimensions()
    {
        // SORA supported resolutions: (480, 480), (854, 480), (720, 720), (1280, 720), (1080, 1080), (1920, 1080)
        return SelectedOrientation switch
        {
            "Horizontal" => SelectedResolution switch
            {
                "480" => (854, 480),    // Horizontal 480p
                "720" => (1280, 720),   // Horizontal 720p (HD)
                "1080" => (1920, 1080), // Horizontal 1080p (Full HD)
                _ => (1280, 720)        // Default to 720p horizontal
            },
            "Vertical" => SelectedResolution switch
            {
                "480" => (480, 854),    // Vertical 480p
                "720" => (720, 1280),   // Vertical 720p
                "1080" => (1080, 1920), // Vertical 1080p
                _ => (720, 1280)        // Default to 720p vertical
            },
            "Square" => SelectedResolution switch
            {
                "480" => (480, 480),    // Square 480p
                "720" => (720, 720),    // Square 720p
                "1080" => (1080, 1080), // Square 1080p
                _ => (720, 720)         // Default to 720p square
            },
            _ => (1280, 720)            // Default to horizontal 720p
        };
    }
}
				
			
Now, let's talk about the page that allows modifying the enhanced prompt for video generation.

The Final Prompt Settings Page

Whether the user selected the option to enhance the video or not, they will be redirected to the VideoPromptsPage.xaml. This page contains a series of mini prompts that will be added to the final prompt to create an amazing video. These mini prompts can be modified if desired and are essentially a set of prompts that define certain characteristics for creating viral videos.

Similarly, this page is responsible for generating the video, so transitioning from this to the next page may take a little while due to the enormous processing required to generate a video.

The page looks as follows:
Final Prompt Settings Page
The XAML code for this page is relatively simple, and it can be modified to add other features that we may need:
				
					<!--  Grid to allow overlay  -->
<Grid>
    <!--  Main Content  -->
    <ScrollView>
        <StackLayout Padding="20" Spacing="15">

            <!--  Header Section  -->
            <Border BackgroundColor="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" StrokeThickness="0">
                <Border.StrokeShape>
                    <RoundRectangle CornerRadius="12" />
                </Border.StrokeShape>
                <StackLayout Padding="20" Spacing="10">
                    <Label
                        FontAttributes="Bold"
                        FontSize="24"
                        Text="Generated Prompts"
                        TextColor="White" />
                    <Label
                        FontSize="16"
                        Opacity="0.9"
                        Text="{Binding VideoIdea}"
                        TextColor="White" />
                </StackLayout>
            </Border>

            <!--  Statistics  -->
            <Grid ColumnDefinitions="*,*" ColumnSpacing="10">
                <Border
                    Grid.Column="0"
                    BackgroundColor="{AppThemeBinding Light=#F0F0F0,
                                                        Dark=#2A2A2A}"
                    StrokeThickness="0">
                    <Border.StrokeShape>
                        <RoundRectangle CornerRadius="8" />
                    </Border.StrokeShape>
                    <StackLayout Padding="15" Spacing="5">
                        <Label
                            FontAttributes="Bold"
                            FontSize="24"
                            HorizontalOptions="Center"
                            Text="{Binding TotalPrompts}" />
                        <Label
                            FontSize="12"
                            HorizontalOptions="Center"
                            Opacity="0.7"
                            Text="Total Prompts" />
                    </StackLayout>
                </Border>

                <Border
                    Grid.Column="1"
                    BackgroundColor="{AppThemeBinding Light=#F0F0F0,
                                                        Dark=#2A2A2A}"
                    StrokeThickness="0">
                    <Border.StrokeShape>
                        <RoundRectangle CornerRadius="8" />
                    </Border.StrokeShape>
                    <StackLayout Padding="15" Spacing="5">
                        <Label
                            FontAttributes="Bold"
                            FontSize="24"
                            HorizontalOptions="Center"
                            Text="{Binding EditedPrompts}"
                            TextColor="{AppThemeBinding Light={StaticResource Primary},
                                                        Dark={StaticResource PrimaryDark}}" />
                        <Label
                            FontSize="12"
                            HorizontalOptions="Center"
                            Opacity="0.7"
                            Text="Edited" />
                    </StackLayout>
                </Border>
            </Grid>

            <!--  Prompts List  -->
            <CollectionView ItemsSource="{Binding Prompts}" SelectionMode="None">
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="viewmodels:VideoPrompt">
                        <Border
                            Margin="0,5"
                            BackgroundColor="{AppThemeBinding Light=White,
                                                                Dark=#1A1A1A}"
                            Stroke="{AppThemeBinding Light=#E0E0E0,
                                                        Dark=#404040}"
                            StrokeThickness="1">
                            <Border.StrokeShape>
                                <RoundRectangle CornerRadius="12" />
                            </Border.StrokeShape>
                            <StackLayout Padding="15" Spacing="10">

                                <!--  Header with title and edited indicator  -->
                                <Grid ColumnDefinitions="*,Auto">
                                    <Label
                                        Grid.Column="0"
                                        FontAttributes="Bold"
                                        FontSize="18"
                                        Text="{Binding Title}" />
                                    <Label
                                        Grid.Column="1"
                                        FontSize="12"
                                        IsVisible="{Binding IsEdited}"
                                        Text="✏️ Edited"
                                        TextColor="{AppThemeBinding Light={StaticResource Primary},
                                                                    Dark={StaticResource PrimaryDark}}" />
                                </Grid>

                                <!--  Content  -->
                                <Label
                                    FontSize="16"
                                    LineBreakMode="WordWrap"
                                    Text="{Binding Content}" />

                                <!--  Action Buttons  -->
                                <Grid
                                    Margin="0,10,0,0"
                                    ColumnDefinitions="*,*,*"
                                    ColumnSpacing="10">
                                    <Button
                                        Grid.Column="0"
                                        BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
                                                                            Dark={StaticResource PrimaryDark}}"
                                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:VideoPromptsViewModel}}, Path=EditPromptCommand}"
                                        CommandParameter="{Binding .}"
                                        FontSize="14"
                                        Text="Edit"
                                        TextColor="White" />

                                    <Button
                                        Grid.Column="1"
                                        BackgroundColor="#FF9500"
                                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:VideoPromptsViewModel}}, Path=RegeneratePromptCommand}"
                                        CommandParameter="{Binding .}"
                                        FontSize="14"
                                        Text="🔄 Regenerate"
                                        TextColor="White" />

                                    <Button
                                        Grid.Column="2"
                                        BackgroundColor="#FF3B30"
                                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:VideoPromptsViewModel}}, Path=DeletePromptCommand}"
                                        CommandParameter="{Binding .}"
                                        FontSize="14"
                                        Text="🗑️"
                                        TextColor="White" />
                                </Grid>
                            </StackLayout>
                        </Border>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>

            <!--  Generate Video Button  -->
            <Button
                Margin="0,20,0,0"
                BackgroundColor="{AppThemeBinding Light={StaticResource Success},
                                                    Dark={StaticResource Success}}"
                Command="{Binding GenerateVideoCommand}"
                FontAttributes="Bold"
                FontSize="18"
                HeightRequest="50"
                Text="🎬 Generate Video"
                TextColor="White" />

        </StackLayout>
    </ScrollView>

    <!--  FULLSCREEN BLOCKING OVERLAY  -->
    <ContentView
        BackgroundColor="#80000000"
        InputTransparent="False"
        IsVisible="{Binding IsLoading}">

        <!--  Centered Loading Content  -->
        <StackLayout
            HorizontalOptions="Center"
            Spacing="30"
            VerticalOptions="Center">

            <!--  Loading Animation  -->
            <ActivityIndicator
                HeightRequest="80"
                IsRunning="{Binding IsLoading}"
                WidthRequest="80"
                Color="White" />

            <!--  Loading Card  -->
            <Border
                Padding="30"
                BackgroundColor="White"
                HorizontalOptions="Center"
                StrokeThickness="0">
                <Border.StrokeShape>
                    <RoundRectangle CornerRadius="15" />
                </Border.StrokeShape>

                <StackLayout
                    HorizontalOptions="Center"
                    Spacing="20"
                    WidthRequest="300">

                    <!--  Main Message  -->
                    <Label
                        FontAttributes="Bold"
                        FontSize="20"
                        HorizontalOptions="Center"
                        HorizontalTextAlignment="Center"
                        Text="🎬 Generating Video with AI"
                        TextColor="{StaticResource Primary}" />

                    <!--  Progress Message  -->
                    <Label
                        FontSize="16"
                        HorizontalOptions="Center"
                        HorizontalTextAlignment="Center"
                        LineBreakMode="WordWrap"
                        Text="Your viral video is being created using SORA AI. This process may take several minutes due to the complexity of video generation."
                        TextColor="{StaticResource Gray600}" />

                    <!--  Status Message  -->
                    <Label
                        FontAttributes="Italic"
                        FontSize="14"
                        HorizontalOptions="Center"
                        HorizontalTextAlignment="Center"
                        Text="🔄 Processing..."
                        TextColor="{StaticResource Primary}" />

                    <!--  Warning Message  -->
                    <Label
                        FontAttributes="Bold"
                        FontSize="12"
                        HorizontalOptions="Center"
                        HorizontalTextAlignment="Center"
                        Text="⚠️ Do not close the app during this process"
                        TextColor="#FF9500" />

                </StackLayout>

            </Border>
        </StackLayout>
    </ContentView>

</Grid>
				
			
The ViewModel of this page, called VideoPromptsViewModel, looks as follows:
				
					public partial class VideoPromptsViewModel : ObservableObject, IQueryAttributable
{
    private readonly IChatService _chatService;
    private readonly ISoraService _soraService;

    [ObservableProperty]
    private ObservableCollection<VideoPrompt> prompts = new();

    [ObservableProperty]
    private int totalPrompts = 0;

    [ObservableProperty]
    private int editedPrompts = 0;

    [ObservableProperty]
    private string videoIdea = string.Empty;

    [ObservableProperty]
    private bool isLoading = false;

    // Store original and enhanced ideas
    private string _originalIdea = string.Empty;
    private string _enhancedIdea = string.Empty;
    private bool _wasEnhanced = false;

    // Video configuration properties
    private int _videoWidth = 1280;      // Default horizontal 720p
    private int _videoHeight = 720;
    private int _videoDuration = 10;     // Default 10 seconds
    private string _videoOrientation = "Horizontal";

    public VideoPromptsViewModel(IChatService chatService, ISoraService soraService)
    {
        _chatService = chatService;
        _soraService = soraService;
        // Don't load sample data by default anymore
    }

    // IQueryAttributable implementation
    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.ContainsKey("VideoIdea"))
        {
            _originalIdea = query["VideoIdea"].ToString() ?? "";
        }

        if (query.ContainsKey("EnhancedIdea"))
        {
            _enhancedIdea = query["EnhancedIdea"].ToString() ?? "";
            _wasEnhanced = !string.IsNullOrEmpty(_enhancedIdea);
        }

        if (query.ContainsKey("WasEnhanced"))
        {
            _wasEnhanced = bool.Parse(query["WasEnhanced"].ToString() ?? "false");
        }

        // Get video configuration parameters
        if (query.ContainsKey("VideoWidth"))
        {
            int.TryParse(query["VideoWidth"].ToString(), out _videoWidth);
        }

        if (query.ContainsKey("VideoHeight"))
        {
            int.TryParse(query["VideoHeight"].ToString(), out _videoHeight);
        }

        if (query.ContainsKey("VideoDuration"))
        {
            int.TryParse(query["VideoDuration"].ToString(), out _videoDuration);
        }

        if (query.ContainsKey("VideoOrientation"))
        {
            _videoOrientation = query["VideoOrientation"].ToString() ?? "Horizontal";
        }

        // Set the display idea (enhanced if available, otherwise original)
        VideoIdea = _wasEnhanced && !string.IsNullOrEmpty(_enhancedIdea) ? _enhancedIdea : _originalIdea;

        // Generate prompts based on the video idea
        _ = GeneratePromptsFromAI();
    }

    private async Task GeneratePromptsFromAI()
    {
        if (string.IsNullOrWhiteSpace(VideoIdea))
        {
            LoadSampleData(); // Fallback to sample data
            return;
        }

        IsLoading = true;
        Prompts.Clear();

        try
        {
            var isConfigured = await _chatService.IsConfiguredAsync();
            if (!isConfigured)
            {
                LoadSampleData(); // Fallback to sample data
                return;
            }

            // Generate prompts using AI
            var generatedPrompts = await _chatService.GenerateVideoPromptsAsync(VideoIdea, _wasEnhanced);
            
            int promptId = 1;
            foreach (var prompt in generatedPrompts)
            {
                Prompts.Add(new VideoPrompt
                {
                    Id = promptId++,
                    Title = prompt.Title,
                    Content = prompt.Content,
                    IsEdited = false
                });
            }

            UpdateStatistics();
        }
        catch (Exception)
        {
            // If AI generation fails, fall back to sample data
            LoadSampleData();
        }
        finally
        {
            IsLoading = false;
        }
    }

    private void LoadSampleData()
    {
        VideoIdea = "Dancing cat compilation with trending music";
        
        Prompts.Add(new VideoPrompt
        {
            Id = 1,
            Title = "Opening Hook",
            Content = "Start with the most energetic cat dance move synchronized to the beat drop",
            IsEdited = false
        });

        Prompts.Add(new VideoPrompt
        {
            Id = 2,
            Title = "Main Content",
            Content = "Sequence of 5-7 different cats dancing to trending audio, each for 3-4 seconds",
            IsEdited = false
        });

        Prompts.Add(new VideoPrompt
        {
            Id = 3,
            Title = "Engagement Boost",
            Content = "Add text overlay: 'Which cat has the best moves? 👇 Comment below!'",
            IsEdited = false
        });

        Prompts.Add(new VideoPrompt
        {
            Id = 4,
            Title = "Call to Action",
            Content = "End with 'Follow for more dancing pets!' with heart and dancing emojis",
            IsEdited = false
        });

        UpdateStatistics();
    }

    [RelayCommand]
    private async Task EditPrompt(VideoPrompt prompt)
    {
        try
        {
            var page = GetCurrentPage();
            if (page == null) return;

            var result = await page.DisplayPromptAsync(
                "Edit Prompt", 
                $"Edit the {prompt.Title}:", 
                "Save", 
                "Cancel", 
                placeholder: prompt.Content, 
                initialValue: prompt.Content);

            if (!string.IsNullOrEmpty(result) && result != prompt.Content)
            {
                if (!prompt.IsEdited)
                {
                    prompt.IsEdited = true;
                    EditedPrompts++;
                }
                prompt.Content = result;
            }
        }
        catch
        {
            // Handle any navigation errors silently
        }
    }

    [RelayCommand]
    private void DeletePrompt(VideoPrompt prompt)
    {
        if (prompt.IsEdited)
        {
            EditedPrompts--;
        }
        Prompts.Remove(prompt);
        UpdateStatistics();
    }

    [RelayCommand]
    private async Task RegeneratePrompt(VideoPrompt prompt)
    {
        // Simulate regeneration
        await Task.Delay(1000);
        
        var regeneratedContent = prompt.Title switch
        {
            "Opening Hook" => "Begin with an unexpected cat appearing from behind furniture, dancing immediately",
            "Main Content" => "Create a montage of cats from different breeds, each showcasing unique dance styles",
            "Engagement Boost" => "Add interactive text: 'Tap ❤️ if your cat can dance too!'",
            "Call to Action" => "Conclude with 'Tag a friend who loves dancing cats!' with music note emojis",
            _ => "Regenerated prompt content"
        };

        if (!prompt.IsEdited)
        {
            prompt.IsEdited = true;
            EditedPrompts++;
        }
        prompt.Content = regeneratedContent;
    }

    [RelayCommand]
    private async Task GenerateVideo()
    {
        try
        {
            Debug.WriteLine("[VideoPrompts] GenerateVideo started");
            var page = GetCurrentPage();
            
            // Check if SORA service is configured
            var isConfigured = await _soraService.IsConfiguredAsync();
            Debug.WriteLine($"[VideoPrompts] SORA configured: {isConfigured}");
            
            if (!isConfigured)
            {
                Debug.WriteLine("[VideoPrompts] SORA not configured, showing alert");
                if (page != null)
                {
                    await page.DisplayAlert("Configuration Required", 
                        "Please configure your SORA credentials in the settings page.", 
                        "OK");
                }
                return;
            }

            // Activate loading UI (blocks all interaction)
            IsLoading = true;
            Debug.WriteLine("[VideoPrompts] Loading UI activated - blocking all interaction");

            // Generate the combined prompt from all prompts
            var combinedPrompt = string.Join(". ", Prompts.Select(p => p.Content));
            if (string.IsNullOrWhiteSpace(combinedPrompt))
            {
                combinedPrompt = VideoIdea; // Fallback to original idea
            }

            Debug.WriteLine($"[VideoPrompts] Combined prompt: {combinedPrompt}");
            Debug.WriteLine($"[VideoPrompts] Video configuration: {_videoWidth}x{_videoHeight}, {_videoDuration}s, {_videoOrientation}");
            Debug.WriteLine("[VideoPrompts] Starting SORA video generation...");

            // Generate video using SORA service with custom configuration
            var videoFilePath = await _soraService.GenerateVideoAsync(combinedPrompt, _videoWidth, _videoHeight, _videoDuration);
            
            Debug.WriteLine($"[VideoPrompts] ✅ Video generated successfully: {videoFilePath}");
            Debug.WriteLine($"[VideoPrompts] File exists: {File.Exists(videoFilePath)}");
            Debug.WriteLine($"[VideoPrompts] File size: {new FileInfo(videoFilePath).Length / 1024 / 1024:F2} MB");

            // Prepare navigation parameters
            var navigationParams = new Dictionary<string, object>
            {
                ["VideoTitle"] = VideoIdea,
                ["VideoSource"] = videoFilePath,
                ["OriginalPrompt"] = _originalIdea,
                ["EnhancedPrompt"] = _wasEnhanced ? _enhancedIdea : "",
                ["EnglishContent"] = combinedPrompt,
                ["HasEnhancedPrompt"] = _wasEnhanced.ToString()
            };

            Debug.WriteLine("[VideoPrompts] Navigation parameters:");
            foreach (var kvp in navigationParams)
            {
                Debug.WriteLine($"[VideoPrompts]   {kvp.Key}: {kvp.Value}");
            }

            // Navigate to video display page with real video
            Debug.WriteLine("[VideoPrompts] Navigating to VideoDisplay page...");
            await Shell.Current.GoToAsync("VideoDisplay", navigationParams);

            // Show success message
            if (page != null)
            {
                Debug.WriteLine("[VideoPrompts] Showing success message");
                await page.DisplayAlert("Video Generated!", 
                    $"Your viral video has been created successfully.\n\nFile: {Path.GetFileName(videoFilePath)}", 
                    "Watch Video");
            }
            
            Debug.WriteLine("[VideoPrompts] GenerateVideo completed successfully");
        }
        catch (InvalidOperationException ex)
        {
            Debug.WriteLine($"[VideoPrompts] Configuration error: {ex.Message}");
            var page = GetCurrentPage();
            if (page != null)
            {
                await page.DisplayAlert("Configuration Error", ex.Message, "OK");
            }
        }
        catch (TimeoutException)
        {
            Debug.WriteLine("[VideoPrompts] Timeout error");
            var page = GetCurrentPage();
            if (page != null)
            {
                await page.DisplayAlert("Timeout", 
                    "Video generation took too long. Please try again.", 
                    "OK");
            }
        }
        catch (HttpRequestException ex)
        {
            Debug.WriteLine($"[VideoPrompts] HTTP error: {ex.Message}");
            var page = GetCurrentPage();
            if (page != null)
            {
                await page.DisplayAlert("Connection Error", 
                    $"Failed to connect to the SORA service: {ex.Message}", 
                    "OK");
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[VideoPrompts] Unexpected error: {ex.Message}");
            Debug.WriteLine($"[VideoPrompts] Exception type: {ex.GetType().Name}");
            Debug.WriteLine($"[VideoPrompts] Stack trace: {ex.StackTrace}");
            var page = GetCurrentPage();
            if (page != null)
            {
                await page.DisplayAlert("Error", $"An error occurred while generating the video: {ex.Message}", "OK");
            }
        }
        finally
        {
            // Always deactivate loading UI regardless of success or failure
            IsLoading = false;
            Debug.WriteLine("[VideoPrompts] Loading UI deactivated - interaction restored");
        }
    }


    private Page? GetCurrentPage()
    {
        try
        {
            return Application.Current?.Windows?.FirstOrDefault()?.Page;
        }
        catch
        {
            return null;
        }
    }

    private void UpdateStatistics()
    {
        TotalPrompts = Prompts.Count;
        EditedPrompts = Prompts.Count(p => p.IsEdited);
    }
}

public partial class VideoPrompt : ObservableObject
{
    [ObservableProperty]
    private int id;

    [ObservableProperty]
    private string title = string.Empty;

    [ObservableProperty]
    private string content = string.Empty;

    [ObservableProperty]
    private bool isEdited;
}
				
			

Page with the Result of the Final Video Generation

The last page of the application is the one that displays the generated video called `VideoDisplayPage.xaml`. This page utilizes the sleek and enhanced MediaElement, which is a super cross-platform control that allows for very simple multimedia element display developed by the .NET community. Similarly, within this page, I have left some elements as placeholders, which would allow for video regeneration, sharing the video on social media, or downloading the video, that you can adapt according to your needs.
The video viewer page for videos generated by the SORA model
The code for this page is as follows:
				
					<ScrollView>
    <VerticalStackLayout Padding="20" Spacing="15">

        <!--  Loading Indicator  -->
        <VerticalStackLayout
            Padding="30"
            HorizontalOptions="Center"
            IsVisible="{Binding IsLoading}"
            Spacing="20"
            VerticalOptions="Center">

            <ActivityIndicator
                HeightRequest="50"
                HorizontalOptions="Center"
                IsRunning="{Binding IsLoading}"
                WidthRequest="50"
                Color="{StaticResource Primary}" />

            <Label
                FontAttributes="Bold"
                FontSize="18"
                HorizontalOptions="Center"
                Text="Loading your generated video..."
                TextColor="{AppThemeBinding Light={StaticResource Primary},
                                            Dark={StaticResource Primary}}" />

            <Label
                FontSize="14"
                HorizontalOptions="Center"
                Text="Please wait while we prepare your video for playback"
                TextColor="{AppThemeBinding Light={StaticResource Gray600},
                                            Dark={StaticResource Gray400}}" />

        </VerticalStackLayout>

        <!--  Main Content (hidden while loading)  -->
        <VerticalStackLayout IsVisible="{Binding IsLoading, Converter={StaticResource InvertedBoolConverter}}" Spacing="15">

            <!--  Video Title  -->
            <Label
                FontAttributes="Bold"
                FontSize="18"
                HorizontalOptions="Center"
                Text="{Binding VideoTitle}"
                TextColor="White" />

            <!--  MediaElement for video playback  -->
            <Border
                Padding="5"
                BackgroundColor="Black"
                Stroke="Gray"
                StrokeThickness="2">
                <Border.StrokeShape>
                    <RoundRectangle CornerRadius="10" />
                </Border.StrokeShape>

                <toolkit:MediaElement
                    x:Name="VideoPlayer"
                    Aspect="AspectFit"
                    BackgroundColor="Black"
                    HeightRequest="300"
                    ShouldAutoPlay="False"
                    ShouldLoopPlayback="False"
                    ShouldShowPlaybackControls="True"
                    Source="{Binding VideoSource}" />
            </Border>

            <!--  Video Controls  -->
            <HorizontalStackLayout HorizontalOptions="Center" Spacing="20">
                <Button
                    BackgroundColor="#1E90FF"
                    Command="{Binding PlayCommand}"
                    Text="Play"
                    TextColor="White"
                    WidthRequest="80" />
                <Button
                    BackgroundColor="#FF4500"
                    Command="{Binding PauseCommand}"
                    Text="Pause"
                    TextColor="White"
                    WidthRequest="80" />
                <Button
                    BackgroundColor="#DC143C"
                    Command="{Binding StopCommand}"
                    Text="Stop"
                    TextColor="White"
                    WidthRequest="80" />
            </HorizontalStackLayout>

            <!--  Video Information  -->
            <Border
                Padding="15"
                BackgroundColor="#2C2C2C"
                Stroke="Gray"
                StrokeShape="RoundRectangle 10">
                <VerticalStackLayout Spacing="10">
                    <Label
                        FontAttributes="Bold"
                        FontSize="16"
                        Text="Video Information"
                        TextColor="White" />

                    <Label
                        FontSize="14"
                        LineBreakMode="WordWrap"
                        Text="{Binding VideoDescription}"
                        TextColor="LightGray" />

                    <Label
                        FontSize="12"
                        Text="{Binding VideoDuration, StringFormat='Duration: {0}'}"
                        TextColor="LightGray" />

                    <Label
                        FontSize="12"
                        Text="{Binding VideoStatus, StringFormat='Status: {0}'}"
                        TextColor="LightGray" />
                </VerticalStackLayout>
            </Border>

            <!--  Original Prompt Section  -->
            <Border
                Padding="15"
                BackgroundColor="#2C2C2C"
                Stroke="Gray"
                StrokeShape="RoundRectangle 10">
                <VerticalStackLayout Spacing="10">
                    <Label
                        FontAttributes="Bold"
                        FontSize="16"
                        Text="Original Prompt"
                        TextColor="White" />

                    <Label
                        FontSize="14"
                        LineBreakMode="WordWrap"
                        Text="{Binding OriginalPrompt}"
                        TextColor="LightGray" />
                </VerticalStackLayout>
            </Border>

            <!--  Enhanced Prompt Section (if available)  -->
            <Border
                Padding="15"
                BackgroundColor="#2C2C2C"
                IsVisible="{Binding HasEnhancedPrompt}"
                Stroke="Gray"
                StrokeShape="RoundRectangle 10">
                <VerticalStackLayout Spacing="10">
                    <Label
                        FontAttributes="Bold"
                        FontSize="16"
                        Text="AI Enhanced Prompt"
                        TextColor="White" />

                    <Label
                        FontSize="14"
                        LineBreakMode="WordWrap"
                        Text="{Binding EnhancedPrompt}"
                        TextColor="LightGray" />
                </VerticalStackLayout>
            </Border>

            <!--  English Content Section  -->
            <Border
                Padding="15"
                BackgroundColor="#2C2C2C"
                Stroke="Gray"
                StrokeShape="RoundRectangle 10">
                <VerticalStackLayout Spacing="10">
                    <Label
                        FontAttributes="Bold"
                        FontSize="16"
                        Text="English Content"
                        TextColor="White" />

                    <Label
                        FontSize="14"
                        LineBreakMode="WordWrap"
                        Text="{Binding EnglishContent}"
                        TextColor="LightGray" />
                </VerticalStackLayout>
            </Border>

            <!--  Action Buttons  -->
            <HorizontalStackLayout HorizontalOptions="Center" Spacing="15">
                <Button
                    BackgroundColor="#32CD32"
                    Command="{Binding ShareVideoCommand}"
                    Text="Share Video"
                    TextColor="White"
                    WidthRequest="120" />
                <Button
                    BackgroundColor="#4169E1"
                    Command="{Binding DownloadCommand}"
                    Text="Download"
                    TextColor="White"
                    WidthRequest="120" />
                <Button
                    BackgroundColor="#696969"
                    Command="{Binding BackCommand}"
                    Text="Back"
                    TextColor="White"
                    WidthRequest="120" />
            </HorizontalStackLayout>

        </VerticalStackLayout>
        <!--  Closing main content StackLayout  -->

    </VerticalStackLayout>
</ScrollView>
				
			
The code for the ViewModel is as follows:
				
					public partial class VideoDisplayViewModel : ObservableObject, IQueryAttributable
{
    private MediaElement? _mediaElement;

    [ObservableProperty]
    private string videoTitle = "Generated Viral Video";

    [ObservableProperty]
    private string videoSource = "";

    [ObservableProperty]
    private string videoDescription = "AI-generated viral video content";

    [ObservableProperty]
    private string videoDuration = "Loading...";

    [ObservableProperty]
    private string videoStatus = "Ready";

    [ObservableProperty]
    private string originalPrompt = "";

    [ObservableProperty]
    private string enhancedPrompt = "";

    [ObservableProperty]
    private string englishContent = "";

    [ObservableProperty]
    private bool hasEnhancedPrompt = false;

    [ObservableProperty]
    private bool isLoading = true;

    public VideoDisplayViewModel()
    {
        // Sample data for demonstration
        VideoTitle = "AI Generated Viral Video";
        VideoDescription = "This video was generated using AI based on your creative prompt and enhanced with advanced algorithms.";
        OriginalPrompt = "A cat playing with a ball of yarn";
        EnhancedPrompt = "A playful orange tabby cat with bright green eyes gracefully pouncing and rolling around with a large, fluffy ball of rainbow-colored yarn in a cozy, sunlit living room with wooden floors and comfortable furniture.";
        EnglishContent = "Watch as this adorable cat brings joy and entertainment through its playful interaction with colorful yarn. Perfect for social media sharing and guaranteed to make viewers smile!";
        HasEnhancedPrompt = !string.IsNullOrEmpty(EnhancedPrompt);
        
        // For demo purposes, you can use a sample video URL or local file
        // VideoSource = "https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4";
    }

    public void SetMediaElement(MediaElement mediaElement)
    {
        _mediaElement = mediaElement;
        
        // Subscribe to MediaElement events
        if (_mediaElement != null)
        {
            _mediaElement.MediaOpened += OnMediaOpened;
            _mediaElement.MediaEnded += OnMediaEnded;
            _mediaElement.MediaFailed += OnMediaFailed;
        }
    }

    private void OnMediaOpened(object? sender, EventArgs e)
    {
        if (_mediaElement != null)
        {
            VideoDuration = _mediaElement.Duration.ToString(@"mm\:ss");
            VideoStatus = "Ready to play";
            IsLoading = false; // Stop loading when media is ready
        }
    }

    private void OnMediaEnded(object? sender, EventArgs e)
    {
        VideoStatus = "Playback completed";
    }

    private void OnMediaFailed(object? sender, EventArgs e)
    {
        VideoStatus = "Error loading video";
        IsLoading = false; // Stop loading on error
    }

    [RelayCommand]
    private void Play()
    {
        try
        {
            _mediaElement?.Play();
            VideoStatus = "Playing";
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Error playing video: {ex.Message}");
            VideoStatus = "Error playing video";
        }
    }

    [RelayCommand]
    private void Pause()
    {
        try
        {
            _mediaElement?.Pause();
            VideoStatus = "Paused";
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Error pausing video: {ex.Message}");
        }
    }

    [RelayCommand]
    private void Stop()
    {
        try
        {
            _mediaElement?.Stop();
            VideoStatus = "Stopped";
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Error stopping video: {ex.Message}");
        }
    }

    [RelayCommand]
    private async Task ShareVideo()
    {
        try
        {
            if (!string.IsNullOrEmpty(VideoSource))
            {
                await Shell.Current.DisplayAlert("Share Video", $"Sharing: {VideoTitle}\n\nCheck out this AI-generated viral video!", "OK");
            }
            else
            {
                await Shell.Current.DisplayAlert("Info", "No video available to share", "OK");
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Error sharing video: {ex.Message}");
            await Shell.Current.DisplayAlert("Error", "Unable to share video", "OK");
        }
    }

    [RelayCommand]
    private async Task Download()
    {
        try
        {
            // In a real app, this would download the video file
            await Shell.Current.DisplayAlert("Info", "Download functionality will be implemented with actual video generation service", "OK");
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Error downloading video: {ex.Message}");
            await Shell.Current.DisplayAlert("Error", "Unable to download video", "OK");
        }
    }

    [RelayCommand]
    private async Task Back()
    {
        try
        {
            // Stop video before navigating back
            _mediaElement?.Stop();
            await Shell.Current.GoToAsync("..");
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Error navigating back: {ex.Message}");
        }
    }

    // Method to set video data from external source
    public void SetVideoData(string title, string source, string originalPrompt, string enhancedPrompt = "", string englishContent = "")
    {
        VideoTitle = title;
        VideoSource = source;
        OriginalPrompt = originalPrompt;
        EnhancedPrompt = enhancedPrompt;
        EnglishContent = englishContent;
        HasEnhancedPrompt = !string.IsNullOrEmpty(enhancedPrompt);
    }

    // IQueryAttributable implementation
    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        Debug.WriteLine("[VideoDisplay] ApplyQueryAttributes called");
        Debug.WriteLine($"[VideoDisplay] Received {query.Count} parameters:");
        
        // Start loading when receiving new video parameters
        IsLoading = true;
        
        foreach (var kvp in query)
        {
            Debug.WriteLine($"[VideoDisplay]   {kvp.Key}: {kvp.Value}");
        }

        if (query.ContainsKey("VideoTitle"))
        {
            VideoTitle = query["VideoTitle"].ToString() ?? "";
            Debug.WriteLine($"[VideoDisplay] Set VideoTitle: {VideoTitle}");
        }

        if (query.ContainsKey("VideoSource"))
        {
            var videoPath = query["VideoSource"].ToString() ?? "";
            Debug.WriteLine($"[VideoDisplay] Received VideoSource: {videoPath}");
            
            // Validate that the file exists
            if (File.Exists(videoPath))
            {
                VideoSource = videoPath;
                Debug.WriteLine($"[VideoDisplay] ✅ Video file exists, set VideoSource: {VideoSource}");
            }
            else
            {
                Debug.WriteLine($"[VideoDisplay] ❌ Video file not found: {videoPath}");
                // Fallback to sample video for demo
                VideoSource = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
                Debug.WriteLine($"[VideoDisplay] Using fallback video: {VideoSource}");
            }
        }
        else
        {
            Debug.WriteLine("[VideoDisplay] No VideoSource parameter received, using fallback");
            VideoSource = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
        }

        if (query.ContainsKey("OriginalPrompt"))
        {
            OriginalPrompt = query["OriginalPrompt"].ToString() ?? "";
            Debug.WriteLine($"[VideoDisplay] Set OriginalPrompt: {OriginalPrompt}");
        }

        if (query.ContainsKey("EnhancedPrompt"))
        {
            EnhancedPrompt = query["EnhancedPrompt"].ToString() ?? "";
            HasEnhancedPrompt = !string.IsNullOrEmpty(EnhancedPrompt);
            Debug.WriteLine($"[VideoDisplay] Set EnhancedPrompt: {EnhancedPrompt}");
            Debug.WriteLine($"[VideoDisplay] HasEnhancedPrompt: {HasEnhancedPrompt}");
        }

        if (query.ContainsKey("HasEnhancedPrompt"))
        {
            if (bool.TryParse(query["HasEnhancedPrompt"].ToString(), out bool hasEnhanced))
            {
                HasEnhancedPrompt = hasEnhanced;
                Debug.WriteLine($"[VideoDisplay] Set HasEnhancedPrompt from parameter: {HasEnhancedPrompt}");
            }
        }

        if (query.ContainsKey("EnglishContent"))
        {
            EnglishContent = query["EnglishContent"].ToString() ?? "";
            Debug.WriteLine($"[VideoDisplay] Set EnglishContent: {EnglishContent}");
        }

        VideoDescription = $"AI-generated viral video: {VideoTitle}";
        Debug.WriteLine($"[VideoDisplay] Set VideoDescription: {VideoDescription}");
        Debug.WriteLine("[VideoDisplay] ApplyQueryAttributes completed");
    }
}
				
			
With the above, we complete the application's code.

Complete Application Flow

As a final step, I share an image showing the entire process from the prompt input to the generation of the final video:
You can find the full repository on GitHub: ViralVideosDemo Repo
 
I hope you found the article enjoyable.

Best regards!