Commands/Sources/Set-OBSVLCSource.ps1

function Set-OBSVLCSource {
    <#
    
    .SYNOPSIS
        Adds a VLC playlist source
    .DESCRIPTION
        Adds or sets VLC playlist sources to OBS.
        VLC must be installed for this to work.
    .EXAMPLE
        Set-OBSVLCSource -FilePath .\*.mp3 # Creates a playlist of all MP3s in the current directory
    .LINK
        Add-OBSInput
    .LINK
        Set-OBSInputSettings
    
    #>

            
    [Alias('Add-OBSVLCSource','Set-OBSPlaylistSource','Add-OBSPlaylistSource')]    
    [CmdletBinding()]
    param(
    # The path to the media file.
    [Parameter(Mandatory,ValueFromPipelineByPropertyName)]    
    [Alias('FullName','LocalFile','local_file','Playlist')]
    [string[]]
    $FilePath,
    # What to select in the playlist.
    # If a number is provided, this will select an index.
    # If a string is provided, this will select the whole name or last part of a name, accepting wildcards.
    # If an `[IO.FileInfo]` is provided, this will be the exact file.
    [Parameter(ValueFromPipelineByPropertyName)]
    [ValidateScript({
    $validTypeList = [System.Int32],[System.String],[System.IO.FileInfo]
    $thisType = $_.GetType()
    $IsTypeOk =
        $(@( foreach ($validType in $validTypeList) {
            if ($_ -as $validType) {
                $true;break
            }
        }))
    if (-not $isTypeOk) {
        throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string','System.IO.FileInfo'."
    }
    return $true
    })]
    
    [Alias('SelectIndex','SelectName')]
    $Select,
    # If set, will shuffle the playlist
    [Parameter(ValueFromPipelineByPropertyName)]
    [ComponentModel.DefaultBindingProperty("shuffle")]
    [switch]
    $Shuffle,
    # If set, the playlist will loop.
    [Parameter(ValueFromPipelineByPropertyName)]
    [ComponentModel.DefaultBindingProperty("loop")]
    [Alias('Looping')]
    [switch]
    $Loop,
    # If set, will show subtitles, if available.
    [Parameter(ValueFromPipelineByPropertyName)]
    [ComponentModel.DefaultBindingProperty("subtitle_enable")]
    [Alias('ShowSubtitles','Subtitles')]
    [switch]
    $Subtitle,
    # The selected audio track number.
    [Parameter(ValueFromPipelineByPropertyName)]
    [ComponentModel.DefaultBindingProperty("track")]    
    [int]
    $AudioTrack,
    # The selected subtitle track number.
    [Parameter(ValueFromPipelineByPropertyName)]
    [ComponentModel.DefaultBindingProperty("subtitle")]    
    [int]
    $SubtitleTrack,
    # The name of the scene.
    # If no scene name is provided, the current program scene will be used.
    [Parameter(ValueFromPipelineByPropertyName)]
    [string]
    $Scene,
    # The name of the input.
    # If no name is provided, the last segment of the URI or file path will be the input name.
    [Parameter(ValueFromPipelineByPropertyName)]
    [string]
    $Name,
    # If set, will check if the source exists in the scene before creating it and removing any existing sources found.
    # If not set, you will get an error if a source with the same name exists.
    [Parameter(ValueFromPipelineByPropertyName)]
    [switch]
    $Force,
    # If set, will fit the input to the screen.
    [Parameter(ValueFromPipelineByPropertyName)]
    [switch]
    $FitToScreen
    )
    dynamicParam {
    $baseCommand = 
        if (-not $script:AddOBSInput) {
            $script:AddOBSInput = 
                $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function')
            $script:AddOBSInput
        } else {
            $script:AddOBSInput
        }
    $IncludeParameter = @()
    $ExcludeParameter = 'inputKind','sceneName','inputName'
    $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new()            
    :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) {
        if ($ExcludeParameter) {
            foreach ($exclude in $ExcludeParameter) {
                if ($paramName -like $exclude) { continue nextInputParameter}
            }
        }
        if ($IncludeParameter) {
            $shouldInclude = 
                foreach ($include in $IncludeParameter) {
                    if ($paramName -like $include) { $true;break}
                }
            if (-not $shouldInclude) { continue nextInputParameter }
        }
        
        $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new(
            $baseCommand.Parameters[$paramName].Name,
            $baseCommand.Parameters[$paramName].ParameterType,
            $baseCommand.Parameters[$paramName].Attributes
        ))
    }
    $DynamicParameters
    }
        begin {
        filter OutputAndFitToScreen {
                    if ($FitToScreen -and $_.FitToScreen) {
                        $_.FitToScreen()
                    }
                    $_
                
        }
    
    }
        process {
        $myParameters = [Ordered]@{} + $PSBoundParameters
        
        if (-not $myParameters["Scene"]) {
            $myParameters["Scene"] = Get-OBSCurrentProgramScene
        }
                
        $myParameterData = [Ordered]@{}
        foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) {
            $bindToPropertyName = $null            
            
            foreach ($attribute in $parameter.Attributes) {
                if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) {
                    $bindToPropertyName = $attribute.Name
                    break
                }
            }
            if (-not $bindToPropertyName) { continue }
            if ($myParameters.Contains($parameter.Name)) {
                $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name]
                if ($myParameters[$parameter.Name] -is [switch]) {
                    $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool]
                }
            }
        }
        if (-not (Test-Path $FilePath)) {
            return
        }
        $allPaths = @(foreach ($path in $FilePath) {
            foreach ($_ in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($path)) {
                $_.Path
            }
        })
        $playlistObject = @(
            $currentIndex = 0 
            foreach ($path in $allPaths) {
                $currentIndex++
                $selected = $Select -and (
                    ($select -is [int] -and $currentIndex -eq $select) -or
                    ($select -is [IO.FileInfo] -and $path -eq $select.FullName) -or
                    ($select -is [string] -and 
                        ($path -like $select -or ($path | Split-Path -Leaf) -like $Select)
                    )
                )
                [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$path}
            }
        )
        $myParameterData['playlist'] = $playlistObject
 
        if (-not $Name) {
            $Name = $myParameters["Name"] = $FilePathItem.Name
        }
        $addSplat = [Ordered]@{
            sceneName = $myParameters["Scene"]
            inputKind = "vlc_source"
            inputSettings = $myParameterData
            inputName = $Name
        }
        if ($myParameters.Contains('SceneItemEnabled')) {
            $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool]
        }
        # If -PassThru was passed
        if ($MyParameters["PassThru"]) {
            # pass it down to each command
            $addSplat.Passthru = $MyParameters["PassThru"]
            # If we were called with Add-
            if ($MyInvocation.InvocationName -like 'Add-*') {
                Add-OBSInput @addSplat # passthru Add-OBSInput
            } else {
                # Otherwise, remove SceneItemEnabled, InputKind, and SceneName
                $addSplat.Remove('SceneItemEnabled')
                $addSplat.Remove('inputKind')
                $addSplat.Remove('sceneName')
                # and passthru Set-OBSInputSettings.
                Set-OBSInputSettings @addSplat
            }
            return
        }
        # Add the input.
        $outputAddedResult = Add-OBSInput @addSplat *>&1
        # If we got back an error
        if ($outputAddedResult -is [Management.Automation.ErrorRecord]) {
            # and that error was saying the source already exists,
            if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) {
                # then check if we use the -Force.
                if ($Force)  { # If we do, remove the input
                    Remove-OBSInput -InputName $addSplat.inputName
                    # and re-add our result.
                    $outputAddedResult = Add-OBSInput @addSplat *>&1
                } else {
                    # Otherwise, get the input from the scene,
                    $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] |
                        Where-Object SourceName -eq $myParameters["Name"]
                    # update the input settings
                    $sceneItem.Input.Settings = $addSplat.inputSettings
                    $sceneItem # and return the scene item.
                    $outputAddedResult = $null
                }
            }
            # If the output was still an error
            if ($outputAddedResult -is [Management.Automation.ErrorRecord]) {
                # use $psCmdlet.WriteError so that it shows the error correctly.
                $psCmdlet.WriteError($outputAddedResult)
            }
        }
        # Otherwise, if we had a result
        elseif ($outputAddedResult) {
            # get the input from the scene and optionally fit it to the screen.
            Get-OBSSceneItem -sceneName $myParameters["Scene"] |
                Where-Object SourceName -eq $name |
                OutputAndFitToScreen
        }
    
    }
}