Functions/GenXdev.AI/EnsureFaceRecognition.ps1

################################################################################
<#
.SYNOPSIS
Ensures Docker Desktop and Face Recognition service are installed and running.
 
.DESCRIPTION
This function sets up and manages a face recognition service using Docker.
It ensures that Docker Desktop is installed, builds or uses an existing
face recognition Docker image, and runs the service in a container.
The service uses a specified directory to store face images for recognition.
 
.PARAMETER PathToFaces
The path to the directory where face images are stored. If not specified,
defaults to "$env:USERPROFILE\Pictures\Faces".
 
.PARAMETER Force
If specified, forces rebuilding of Docker image and removing existing data.
 
.EXAMPLE
EnsureFaceRecognition
 
.EXAMPLE
EnsureFaceRecognition -PathToFaces "C:\MyFaces" -Force
#>

function EnsureFaceRecognition {

    [CmdletBinding()]
    param(
        ########################################################################
        [Parameter(
            Position = 0,
            Mandatory = $false,
            HelpMessage = "Path to store face images for recognition"
        )]
        [string] $PathToFaces = $null,

        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Force rebuild of Docker image and remove existing data"
        )]
        [switch] $Force
    )

    begin {
        # set default path for faces if not provided
        if ([string]::IsNullOrWhiteSpace($PathToFaces)) {

            $PathToFaces = "$($env:USERPROFILE)\Pictures\Faces"
        }########################################################################
        <#
        .SYNOPSIS
        Checks if the WinGet PowerShell module is installed.
 
        .DESCRIPTION
        Attempts to import the Microsoft.WinGet.Client module and verifies its
        presence.
 
        .EXAMPLE
        IsWinGetInstalled
        #>

        function IsWinGetInstalled {

            # attempt to load the winget module silently
            Microsoft.PowerShell.Core\Import-Module "Microsoft.WinGet.Client" `
                -ErrorAction SilentlyContinue

            # verify if module was loaded successfully
            $module = Microsoft.PowerShell.Core\Get-Module "Microsoft.WinGet.Client" `
                -ErrorAction SilentlyContinue

            return $null -ne $module
        }        ########################################################################
        <#
        .SYNOPSIS
        Installs the WinGet PowerShell module.
 
        .DESCRIPTION
        Installs and imports the Microsoft.WinGet.Client module for package
        management.
 
        .EXAMPLE
        InstallWinGet
        #>

        function InstallWinGet {
            # install and import winget module with force to ensure success
            Microsoft.PowerShell.Utility\Write-Verbose "Installing WinGet PowerShell client..."
            $null = PowerShellGet\Install-Module "Microsoft.WinGet.Client" `
                -Force `
                -AllowClobber

            # load the newly installed module
            Microsoft.PowerShell.Core\Import-Module "Microsoft.WinGet.Client"
        }

    }    process {

        # store current path to restore it later
        $previousPath = (Microsoft.PowerShell.Management\Get-Location).Path

        # define path for face recognition module files
        $modulesPath = GenXdev.FileSystem\Expand-Path `
            "$($env:APPDATA)\GenXdev\GenXdev.AI\Modules\FaceRecognition"

        GenXdev.AI\EnsureDockerDesktop

        # Check if Docker image exists
        $imageExists = docker images facerec_service --format "{{.Repository}}" 2>$null

        if ($Force -and $imageExists) {
            Microsoft.PowerShell.Utility\Write-Verbose "Removing existing Docker image due to -Force flag..."
            docker rmi facerec_service -f
            wsl --shutdown
            GenXdev.FileSystem\Remove-AllItems $PathToFaces
            $imageExists = $null
        }        if (-not $imageExists) {

            # create modules directory if it doesn't exist
            $null = mkdir $modulesPath -ErrorAction SilentlyContinue
            Microsoft.PowerShell.Management\Push-Location $modulesPath

            try {
                GenXdev.AI\EnsureGithubCLIInstalled                # check if repository already exists
                if ([System.IO.Directory]::Exists("$modulesPath\face_recognition")) {
                    if ($Force) {
                        Microsoft.PowerShell.Utility\Write-Verbose "Updating face recognition repository..."
                        Microsoft.PowerShell.Management\Push-Location .\face_recognition
                        try {
                            git pull origin master
                        }
                        finally {
                            Microsoft.PowerShell.Management\Pop-Location
                        }
                    }
                }
                else {
                    Microsoft.PowerShell.Utility\Write-Verbose "Cloning face recognition repository..."
                    git clone https://github.com/JanLoebel/face_recognition.git
                }

                Microsoft.PowerShell.Management\Set-Location .\face_recognition

                Microsoft.PowerShell.Utility\Write-Verbose "Building Docker image for face recognition service..."
                $buildResult = docker build -t facerec_service . 2>&1                # check if build was successful
                if ($LASTEXITCODE -ne 0) {
                    Microsoft.PowerShell.Utility\Write-Error "Docker build failed with exit code $LASTEXITCODE"
                    Microsoft.PowerShell.Utility\Write-Error ("Build output: $buildResult")
                    throw "Failed to build facerec_service Docker image"
                }                # verify image was created successfully
                $imageExists = docker images facerec_service --format "{{.Repository}}" 2>$null

                if (-not $imageExists) {
                    throw "facerec_service image was not created successfully"
                }

                Microsoft.PowerShell.Utility\Write-Verbose "✅ Docker image 'facerec_service' built successfully"
            }
            catch {
                Microsoft.PowerShell.Utility\Write-Error "Error setting up face recognition: $_"
                throw
            }
            finally {
                Microsoft.PowerShell.Management\Pop-Location
            }
        }
        else {
            Microsoft.PowerShell.Management\Push-Location $modulesPath
            Microsoft.PowerShell.Management\Set-Location .\face_recognition
        }
        $PathToFaces = GenXdev.FileSystem\Expand-Path $PathToFaces -CreateDirectory        # convert windows path to docker volume mount format
        $dockerPath = $PathToFaces

        if ($dockerPath -match '^[A-Za-z]:') {
            # convert C:\path\to\folder to /c/path/to/folder for Docker
            $driveLetter = $dockerPath[0].ToString().ToLower()
            $pathWithoutDrive = $dockerPath.Substring(2).Replace('\', '/')
            $dockerPath = "/$driveLetter$pathWithoutDrive"
        }        # define container name for consistent reference
        $containerName = "facerec_service_container"

        # check if container exists (running or stopped)
        $existingContainer = docker ps -a --filter "name=^${containerName}$" `
            --format "{{.ID}}"

        if ($existingContainer) {
            # check if container is running
            $runningContainer = docker ps --filter "name=^${containerName}$" `
                --format "{{.ID}}"

            if ($runningContainer) {
                Microsoft.PowerShell.Utility\Write-Verbose "facerec_service container is already running."
            }
            else {
                # start existing container
                Microsoft.PowerShell.Utility\Write-Verbose "Starting existing face recognition container..."
                docker start $containerName
                Microsoft.PowerShell.Utility\Start-Sleep -Seconds 2
            }
        }
        else {
            # create and start new container
            Microsoft.PowerShell.Utility\Write-Verbose "Creating and starting face recognition container..."
            docker run -d --name $containerName -p 8080:8080 `
                -v "${dockerPath}:/root/faces" facerec_service
            Microsoft.PowerShell.Utility\Start-Sleep -Seconds 2
        }# set api base url for service communication
        $script:ApiBaseUrl = "http://127.0.0.1:8080"

        Microsoft.PowerShell.Utility\Write-Verbose "Face recognition service should be ready at $script:ApiBaseUrl"        # uncomment this block to enable service readiness check

    }    end {
        # restore original location
        Microsoft.PowerShell.Management\Set-Location $previousPath
    }
}
################################################################################