Install-StreamDeckPlugin.ps1
function Install-StreamDeckPlugin { <# .Synopsis Installs a Stream Deck Plugin .Description Installs a Stream Deck Plugin. This copies the files in the plugin directory to the .Example Install-StreamDeckPlugin -SourcePath .\ScriptDeck.sdPlugin .Link Get-StreamDeckPlugin #> param( # The source path. [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [Alias('FullName')] [string] $SourcePath, # The destination path. This will usually be automatically detected based off of the operating system. [Parameter(ValueFromPipelineByPropertyName)] [string] $DestinationPath, # If set, will display the files copied. [switch] $PassThru ) process { if (-not $DestinationPath) { # If no -DestinationPath was provided $DestinationPath = if ((-not $PSVersionTable.Platform) -or ($PSVersionTable.Platform -match 'Win')) { # use AppData if it's windows "$env:AppData\Elgato\StreamDeck\Plugins\" } elseif ($PSVersionTable.Platform -eq 'Unix') { # or the library if it is a Mac "~/Library/Application Support/elgato/StreamDeck/Plugins" } if (-not $DestinationPath) { # If we could not determine the -DestinationPath Write-Error "Could not determine -DestinationPath." -ErrorId StreamDeck.Destination.Missing return # error out. } } # If we couldn't resolve the destination $resolvedDestinationPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($DestinationPath) if (-not $resolvedDestinationPath) { return } # error out. # If we couldn't resolve the source $resolvedSourcePath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($SourcePath) if (-not $resolvedSourcePath) { return } # error out. # Find the source item $sourcePathItem = $sourceFileInfo = Get-Item -LiteralPath $resolvedSourcePath if ($sourcePathItem -is [IO.FileInfo]) { # If it is a file if ($sourcePathItem.Extension -notin '.zip', '.streamDeckPlugin') { # it must be a .zip or .streamDeckPlugin Write-Error "If -SourcePath is a file, it must be a .streamDeckPlugin or .zip file" -ErrorId StreamDeck.Plugin.Incorrect.Format return } # Find where temp is $tmpPathRoot = if ($env:TEMP) { $env:TEMP } else { "/tmp" } # Create a temporary directory $sourceTempDir = Join-Path $tmpPathRoot $sourcePathItem.Name.Replace($sourcePathItem.Extension, '') if (Test-Path $sourceTempDir) { # If it already existed Remove-Item -Recurse -Force $sourceTempDir # clean it. if (-not $?) { return } # (if that didn't work, error out) } # Extract the plugin to that directory [IO.Compression.ZipFile]::ExtractToDirectory($sourcePathItem.fullname, $sourceTempDir) # Get ready to call yourself $MySplat = @{} + $PSBoundParameters $MySplat.Remove('SourcePath') # determine if the manifest is at root $manifestAtRoot = Get-ChildItem -Path $sourceTempDir -File -Filter manifest.json if (-not $manifestAtRoot) { # If it is not, install any plugin subdirectories Get-ChildItem -Path $sourceTempDir -Directory | Install-StreamDeckPlugin @MySplat } else { # If it is, install just this subdirectory. Install-StreamDeckPlugin @MySplat -sourcePath $manifestAtRoot.Directory } # clean out the temporary files. Remove-Item $sourceTempDir -Recurse -Force return } if ($sourcePathItem -isnot [IO.DirectoryInfo]) { # If the source path item was not a directory Write-Error "-SourcePath must be a directory or a plugin file" -ErrorId StreamDeck.Plugin.Not.Directory return # error out. } # Determine the destination path $DestinationPathLeaf = $resolvedDestinationPath | Split-Path -Leaf if ($DestinationPathLeaf -notlike '*.sdPlugin') { # If it's not already a .sdPlugin directory $newDestPath = Join-Path $resolvedDestinationPath ( $sourcePathItem.Name.Replace("$($sourcePathItem.Extension)",'') + '.sdPlugin' ) # determine the right .sdPlugin path if (-not (Test-Path $newDestPath)) { $createdDirectory = New-Item -ItemType Directory -Path $newDestPath if (-not $createdDirectory) { Write-Error "Could not create plugin directory: '$newDestPath'" return } } $resolvedDestinationPath = $newDestPath } # Get all of the files from source, recursively. $pluginFiles = @(Get-ChildItem $sourcePathItem -Recurse) $c, $t = 0, $pluginFiles.Length $progressId = Get-Random foreach ($pluginFile in $pluginFiles) { # determine it's relative path, $relativePath = $pluginFile.FullName.Replace("$($sourcePathItem.Fullname)$([IO.Path]::DirectorySeparatorChar)", "") $newPath = Join-Path $resolvedDestinationPath $relativePath $null = try { # create the directory structure with New-Item -Force, New-Item -ItemType $( if ($pluginFile -is [IO.FileInfo]){ 'File' } else { 'Directory' } ) -Path $newPath -Force } catch { $_ } $c++ $p = [Math]::Min(100, $c * 100 / $t) # and copy the file. Write-Progress "Copying" "$newPath" -PercentComplete $p -Id $progressId if ($pluginFile -is [IO.FileInfo]) { Copy-Item -Path $pluginFile.Fullname -Destination $newPath -Force -PassThru:$PassThru } } Write-Progress "Copying" "Done" -Completed -Id $progressId } } |