Set-Media.ps1

function Set-Media
{
    <#
    .Synopsis
        Sets media metadata
    .Description
        Sets media metadata, using ffmpeg
    .Example
        Set-Media -InputPath $home\Music\ASong.mp3 -Property @{
            Title = "A Song"
            Artist = "An Artist"
            Lyrics = "Some lyrics"
        }
    .Example
        Set-Media -InputPath $home\Music\ASong.mp3 -AlbumArt $home\Music\Cover.jpg
    .Link
        Get-Media
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([Nullable])]
    param(
    # The Input Path
    [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
    [Alias('Fullname')]
    [string[]]
    $InputPath,

    # A dictionary of properties to update
    [Parameter(ValueFromPipeline)]
    [Alias('InputObject')]
    [PSObject[]]
    $Property,

    # A collection of properties to clear
    [string[]]
    $ClearProperty,

    # The path to FFMpeg.exe. Download it from http://ffmpeg.org/
    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [string]
    $FFMpegPath
    )

    dynamicParam {
        $myCmd = $MyInvocation.MyCommand
        Get-RoughDraftExtension -CommandName $myCmd -DynamicParameter -NoMandatoryDynamicParameter -DynamicParameterSetName "__AllParameterSets" 
    }


    begin {
        $allInputsFiles = [Collections.ArrayList]::new()
        $allInputObjects = [Collections.ArrayList]::new()
        #region Find ffmpeg
        $ffmpeg = Get-FFMpeg -ffMpegPath $ffMpegPath
        #endregion Find ffmpeg

        $xmlPropertyNames = 'LocalName','NamespaceURI','Prefix',
            'NodeType','ParentNode','OwnerDocument','IsEmpty',
            'Attributes','HasAttributes','SchemaInfo',
            'InnerXml','InnerText','NextSibling','PreviousSibling',
            'Value','ChildNodes','FirstChild','LastChild','HasChildNodes',
            'IsReadOnly','OuterXml','BaseURI','PreviousText'
    }

    process {
        $allInputsFiles.AddRange($InputPath)
        if ($Property) {
            $allInputObjects.AddRange($Property)
        }
    }

    end {
        $inputParams = @{} + $PSBoundParameters

        $c, $t, $id = 0, $allInputsFiles.Count, [Random]::new().Next()
        :nextFile foreach ($in in $allInputsFiles) {
            
            #region Resolve the Input Path
            $ri =
                if ([IO.File]::Exists($In)) {
                    $In
                } else {
                    $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($In) |
                        Get-Item -LiteralPath { $_} |
                        Select-Object -ExpandProperty Fullname
                }
            if (-not $ri) { continue }
            #endregion Resolve the Input Path

            $inputObject = $allInputObjects[$c]
            Write-Progress "Setting Metadata" "$ri" -PercentComplete ($c * 100 / $t) -Id $id
            $c++

            $inFile = "$ri"
            $inFileInfo = [IO.FileInfo]$inFile
            $tempFileName = Join-Path $inFileInfo.Directory.FullName ('_temp_' + $inFileInfo.Name)

            $metadataArgs = @(
                if ($ClearProperty -match '\*') {
                    $originalMetadata = Get-Media -InputPath $inFile
                    foreach ($prop in $originalMetadata.psobject.properties) {
                        $matchedFilter = foreach ($cp in $ClearProperty) {
                            if ($prop.Name -like $cp) {
                                $true;break
                            }
                        }

                        if ($matchedFilter) {
                            '-metadata'
                            '' + $prop.Name + '=""'
                        }
                    }
                } else {
                    foreach ($PropertyToClear in $ClearProperty) {
                        '-metadata'
                        '' + $PropertyToClear + '=""'
                    }
                }
                
                if ($inputObject -is [Collections.IDictionary]) {
                    foreach ($kv in $inputObject.GetEnumerator()) {
                        '-metadata'
                        '' + $kv.Key + "=" + (
                            $kv.Value -replace '"','\"' -replace
                                '\[','(' -replace
                                '\]',')' -replace '&', 'and') + " "
                    }
                } elseif ($inputObject -and $inputObject.Fullname -ne $in) {
                    foreach ($prop in $inputObject.psobject.properties) {
                        if ($inputObject -is [Xml.XmlElement] -and
                            $xmlPropertyNames -contains $prop.Name) { continue }
                        '-metadata'
                        '' + $prop.Name + '="' + ($prop.Value -replace '"','\"') + '"'
                    }
                }

                #region Handle Extensions
                Get-RoughDraftExtension -CommandName $myCmd -CanRun -ExtensionParameter $inputParams |
                    . Get-RoughDraftExtension -Run |
                    . { process {
                        $inObj = $_                        
                        if ($inObj.ExtensionOutput) {
                            foreach ($inObjOut in $inObj.extensionOutput) {
                                $inObjOut                                
                            }
                        }
                        if ($inObj.Done) {
                            continue nextFile
                        }
                    } }
                #endregion Handle Extensions
            )

            if ($metadataArgs.Count) {
                if (-not ($metaDataArgs -match '-c(?:\:[av])')) {
                    $metaDataArgs = @('-c', 'copy') + $metaDataArgs
                }
                if ($PSCmdlet.ShouldProcess("$inFile $metadataArgs")) {
                    & $ffmpeg -i "`"$inFile`"" @metadataArgs $tempFileName '-y' 2>&1 |
                        & { process {
                            if ('silentlycontinue', 'ignore' -notcontains $VerbosePreference) {
                                Write-Verbose "$_"
                            }
                        } }

                    if (Test-Path -LiteralPath $tempFileName) {
                        $newFile = Get-Item -LiteralPath $tempFileName
                        if ($newFile.Length) {
                            Move-Item -LiteralPath $tempFileName -Destination $inFile -Force
                        }
                    }
                }
            }

        }


        Write-Progress "Setting Metadata" " " -Completed -Id $id
    }
}