Public/Add-PatPlaylistItem.ps1
|
function Add-PatPlaylistItem { <# .SYNOPSIS Adds items to an existing playlist on a Plex server. .DESCRIPTION Adds one or more media items to an existing playlist. Items are specified by their rating keys (unique identifiers in the Plex library). Items are added to the end of the playlist. .PARAMETER PlaylistId The unique identifier of the playlist to add items to. .PARAMETER PlaylistName The name of the playlist to add items to. Supports tab completion. .PARAMETER RatingKey One or more media item rating keys to add to the playlist. Rating keys can be obtained from library browsing commands like Get-PatLibraryItem. .PARAMETER ServerUri The base URI of the Plex server (e.g., http://plex.example.com:32400). If not specified, uses the default stored server. .PARAMETER PassThru If specified, returns the updated playlist object. .EXAMPLE Add-PatPlaylistItem -PlaylistId 12345 -RatingKey 67890 Adds the media item with rating key 67890 to playlist 12345. .EXAMPLE Add-PatPlaylistItem -PlaylistName 'My Favorites' -RatingKey 111, 222, 333 Adds three items to the playlist named 'My Favorites'. .EXAMPLE Get-PatLibraryItem -SectionId 1 -Filter 'year=2024' | ForEach-Object { $_.ratingKey } | Add-PatPlaylistItem -PlaylistName 'New Releases' Adds all 2024 items from library section 1 to the 'New Releases' playlist. .EXAMPLE Add-PatPlaylistItem -PlaylistId 12345 -RatingKey 67890 -PassThru Adds an item and returns the updated playlist object. .OUTPUTS PlexAutomationToolkit.Playlist (when -PassThru is specified) Returns the updated playlist object showing the new item count. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSReviewUnusedParameter', 'commandName', Justification = 'Standard ArgumentCompleter parameter, not always used' )] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSReviewUnusedParameter', 'parameterName', Justification = 'Standard ArgumentCompleter parameter, not always used' )] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSReviewUnusedParameter', 'commandAst', Justification = 'Standard ArgumentCompleter parameter, not always used' )] [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ById')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'ById')] [ValidateRange(1, [int]::MaxValue)] [int] $PlaylistId, [Parameter(Mandatory = $true, ParameterSetName = 'ByName')] [ValidateNotNullOrEmpty()] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $quoteChar = '' $strippedWord = $wordToComplete if ($wordToComplete -match "^([`"'])(.*)$") { $quoteChar = $Matches[1] $strippedWord = $Matches[2] } $getParams = @{ ErrorAction = 'SilentlyContinue' } if ($fakeBoundParameters.ContainsKey('ServerUri')) { $getParams['ServerUri'] = $fakeBoundParameters['ServerUri'] } $playlists = Get-PatPlaylist @getParams foreach ($playlist in $playlists) { if ($playlist.Title -ilike "$strippedWord*") { $title = $playlist.Title if ($quoteChar) { $text = "$quoteChar$title$quoteChar" } elseif ($title -match '\s') { $text = "'$title'" } else { $text = $title } [System.Management.Automation.CompletionResult]::new( $text, $title, 'ParameterValue', $title ) } } })] [string] $PlaylistName, [Parameter(Mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateRange(1, [int]::MaxValue)] [int[]] $RatingKey, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-PatServerUri -Uri $_ })] [string] $ServerUri, [Parameter(Mandatory = $false)] [switch] $PassThru ) begin { # Use default server if ServerUri not specified $server = $null $effectiveUri = $ServerUri $machineIdentifier = $null if (-not $ServerUri) { try { $server = Get-PatStoredServer -Default -ErrorAction 'Stop' if (-not $server) { throw "No default server configured. Use Add-PatServer with -Default or specify -ServerUri." } $effectiveUri = $server.uri Write-Verbose "Using default server: $effectiveUri" } catch { throw "Failed to get default server: $($_.Exception.Message)" } } else { Write-Verbose "Using specified server: $effectiveUri" } # Build headers with authentication $headers = if ($server) { Get-PatAuthHeaders -Server $server } else { @{ Accept = 'application/json' } } # Get machine identifier for URI construction try { $serverInfoUri = Join-PatUri -BaseUri $effectiveUri -Endpoint '/' $serverInfo = Invoke-PatApi -Uri $serverInfoUri -Headers $headers -ErrorAction 'Stop' $machineIdentifier = $serverInfo.machineIdentifier Write-Verbose "Server machine identifier: $machineIdentifier" } catch { throw "Failed to retrieve server machine identifier: $($_.Exception.Message)" } # Resolve playlist ID if using name $resolvedId = $PlaylistId $playlistInfo = $null if ($PSCmdlet.ParameterSetName -eq 'ByName') { $playlist = Get-PatPlaylist -PlaylistName $PlaylistName -ServerUri $effectiveUri -ErrorAction 'Stop' if (-not $playlist) { throw "No playlist found with name '$PlaylistName'" } $resolvedId = $playlist.PlaylistId $playlistInfo = $playlist } else { try { $playlistInfo = Get-PatPlaylist -PlaylistId $PlaylistId -ServerUri $effectiveUri -ErrorAction 'Stop' } catch { Write-Verbose "Could not retrieve playlist info for ID $PlaylistId" } } # Collect all rating keys from pipeline $allRatingKeys = [System.Collections.ArrayList]::new() } process { # Collect rating keys from pipeline foreach ($key in $RatingKey) { $null = $allRatingKeys.Add($key) } } end { if ($allRatingKeys.Count -eq 0) { Write-Verbose "No rating keys provided, nothing to add" return } # Build descriptive target for confirmation $playlistDesc = if ($playlistInfo) { "'$($playlistInfo.Title)'" } else { "Playlist $resolvedId" } $target = "$($allRatingKeys.Count) item(s) to $playlistDesc" if (-not $PSCmdlet.ShouldProcess($target, 'Add to playlist')) { return } try { # Build the URI with items # Format: server://machineIdentifier/com.plexapp.plugins.library/library/metadata/ratingKey $itemUris = foreach ($key in $allRatingKeys) { "server://$machineIdentifier/com.plexapp.plugins.library/library/metadata/$key" } $uriParam = $itemUris -join ',' $queryString = "uri=$([System.Uri]::EscapeDataString($uriParam))" $endpoint = "/playlists/$resolvedId/items" $uri = Join-PatUri -BaseUri $effectiveUri -Endpoint $endpoint -QueryString $queryString Write-Verbose "Adding $($allRatingKeys.Count) item(s) to playlist $resolvedId" $null = Invoke-PatApi -Uri $uri -Method 'PUT' -Headers $headers -ErrorAction 'Stop' if ($PassThru) { Get-PatPlaylist -PlaylistId $resolvedId -ServerUri $effectiveUri -ErrorAction 'Stop' } } catch { throw "Failed to add items to playlist: $($_.Exception.Message)" } } } |