functions/Invoke-YoutubeDL.ps1

function Invoke-YoutubeDL {
    <#
    .SYNOPSIS
        Invoke youtube-dl
         
    .DESCRIPTION
        Invoke the youtube-dl process, specifying either an already defined job or a configuration file.
         
    .PARAMETER ConfigPath
        The filepath pointing to the configuration file to use.
         
    .PARAMETER JobName
        The name of the job to run.
         
    .EXAMPLE
        PS C:\> Invoke-YoutubeDL -ConfigPath "~/conf.txt" -Url "//some/url/"
         
        Invokes youtube-dl using the specified configuration path, with has an input definition "Url" that is
        passed in as a parameter.
         
    .EXAMPLE
        PS C:\> Invoke-YoutubeDL -JobName "test"
         
        Invokes youtube-dl using the configuration path specified by the job, and any variables which may be
        defined for this job.
         
    .INPUTS
        None
         
    .OUTPUTS
        None
         
    .NOTES
         
         
    #>

    
    [CmdletBinding()]
    param (
        
        [Parameter(Position = 0, Mandatory = $true, ParameterSetName = "Config")]
        [Alias("Path")]
        [string]
        $ConfigPath,
        
        # Tab completion
        [Parameter(Position = 0, Mandatory = $true, ParameterSetName = "Job")]
        [Alias("Job", "Name")]
        [string]
        $JobName
        
    )
    
    dynamicparam {
        
        # Only run the logic if the file exists
        if ($null -ne $ConfigPath -and (Test-Path $ConfigPath) -eq $true) {
            
            # Retrieve all instances of input definitions in the config file
            $definitionList = Read-ConfigDefinitions -Path $ConfigPath -InputDefinitions
            
            #Define the dynamic parameter dictionary to add all new parameters to
            $parameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
            
            # Now that a list of all input definitions is found, create a dynamic parameter for each
            foreach ($definition in $definitionList) {
                
                $paramAttribute = New-Object System.Management.Automation.ParameterAttribute
                $paramAttribute.Mandatory = $true
                
                $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
                $attributeCollection.Add($paramAttribute)                
                $param = New-Object System.Management.Automation.RuntimeDefinedParameter($definition, [String], $attributeCollection)
                
                $parameterDictionary.Add($definition, $param)
                
            }
            
            return $parameterDictionary
            
        }
        
    }
    
    process {
        
        if ($PSCmdlet.ParameterSetName -eq "Config") {
            
            # Ensure the config file actually exists
            if ((Test-Path -Path $ConfigPath) -eq $false) {
                
                Write-Message -Message "There is no file located at: $ConfigPath" -DisplayWarning
                return
                
            }
            
            $configFileContent = Get-Content -Path $ConfigPath -Raw
            
            # Retrieve all instances of input definitions in the config file
            $definitionList = Read-ConfigDefinitions -Path $ConfigPath -InputDefinitions
            
            foreach ($definition in $definitionList) {
                
                if ($PSBoundParameters.ContainsKey($definition) -eq $true) {
                    
                    # Replace the occurence of the input definition with the user provided value
                    $configFileContent = $configFileContent -replace "i@{$definition}", $PSBoundParameters[$definition]
                    
                }else {
                    
                    # Warn the user and exit if they've not specified one of the input definition parameters
                    Write-Message -Message "You have not supplied the following user input: $definition" -DisplayWarning
                    return
                    
                }
                
            }
            
            # Write modified config file (with user inputs) to a temp file
            # It is easier to read in the config file than edit the existing string to work properly, by surrounding stuff in "" quotes etc
            Out-File -FilePath "$script:DataPath\temp.conf" -Force -InputObject $configFileContent
        
        }
        
        if ($PSCmdlet.ParameterSetName -eq "Job") {
            
            # Retrieve the job and heck that it exists
            $jobList = Get-Jobs -Path "$script:DataPath\database.xml"
            $job = $jobList | Where-Object { $_.Name -eq $JobName }
            
            if ($null -eq $job) {
                
                Write-Message -Message "There is no job called: $JobName" -DisplayWarning
                return
                
            }
            
            # Read in the contents of the job config file
            $configFileContent = Get-Content -Path $job.ConfigPath -Raw
            
            # Retrieve all instances of variable definitions in the config file
            $definitionList = Read-ConfigDefinitions -Path $job.ConfigPath -VariableDefinitions
            
            # Check that the job variables match the configuration file definitions, otherwise there would be errors
            $jobDefinitionList = $job.Variables.Keys
            $difference1 = $jobDefinitionList | Where-Object { $definitionList -notcontains $_ }
            $difference2 = $definitionList | Where-Object { $jobDefinitionList -notcontains $_ }
            if (($null -ne $difference1) -or ($null -ne $difference2)) {
                
                Write-Message -Message "The job variables in the database do not match the variable definitions in the configuration file.
                                        `rRun Set-YoutubeDLJob with the -Update switch to fix the issue. See docs for help."
 -DisplayWarning
                return
                
            }
            
            foreach ($definition in $definitionList) {
                
                # Replace the occurence of the variable definition with the variable value from the database
                $configFileContent = $configFileContent -replace "v@{$definition}{start{(?s)(.*?)}end}", $job.Variables[$definition]
                
            }
            
            # Retrieve all instances of variable scriptblocks in the config file
            $scriptblockDefinitionList = Read-ConfigDefinitions -Path $job.ConfigPath -VariableScriptblocks
            
            # Create a table linking each scriptblock to its respective definition name
            [hashtable]$scriptblockList = [ordered]@{}    
            for ($i = 0; $i -lt $definitionList.Count; $i++) {
                
                $scriptblockList.Add($definitionList[$i], [scriptblock]::Create($scriptblockDefinitionList[$i]))
                
            }
            
            # Write modified config file (with user inputs) to a temp file
            # It is easier to read in the config file than edit the existing string to work properly, by surrounding stuff in "" quotes etc
            Out-File -FilePath "$script:DataPath\temp.conf" -Force -InputObject $configFileContent
            
        }
        
        # Define youtube-dl process information
        $processStartupInfo = New-Object System.Diagnostics.ProcessStartInfo -Property @{
            FileName = "youtube-dl"
            Arguments = "--config-location `"$script:DataPath\temp.conf`""
            UseShellExecute = $false
        }
        
        # Start and wait for youtube-dl to finish
        $process = New-Object System.Diagnostics.Process
        $process.StartInfo = $processStartupInfo
        $process.Start()
        
        $process.WaitForExit()
        $process.Dispose()
        
        # Delete the temp config file since its no longer needed
        Remove-Item -Path "$script:DataPath\temp.conf" -Force
        
        # Execute any scriptblocks for variables
        if ($PSCmdlet.ParameterSetName -eq "Job") {
            
            # Run every scriptblock, and store the result back into the databasee
            foreach ($key in $scriptblockList.Keys) {
                
                $returnResult = Invoke-Command -ScriptBlock $scriptblockList[$key]
                
                if ($null -eq $returnResult) {
                    
                    Write-Message -Message "The scriptblock for the $key variable definition didn't return a value. It must return a value." -DisplayError
                    return
                    
                }
                
                $job.Variables[$key] = $returnResult
                
            }
            
            # If the job has a scriptblock, run it
            if ($null -ne $job.Scriptblock) {
                
                Invoke-Command -ScriptBlock $job.Scriptblock -ArgumentList $job
                
            }
            
            # Save the modified job (if any scriptblocks ran) to the database file
            Export-Clixml -Path "$script:DataPath\database.xml" -InputObject $jobList | Out-Null
            
        }
        
        
    }
    
    
}