Public/New-PatPlaylist.ps1

function New-PatPlaylist {
    <#
    .SYNOPSIS
        Creates a new playlist on a Plex server.
 
    .DESCRIPTION
        Creates a new regular (non-smart) playlist on the Plex server. You can specify
        the playlist title, type (video, audio, or photo), and optionally add initial
        items during creation.
 
    .PARAMETER Title
        The title/name of the new playlist. Must be unique on the server.
 
    .PARAMETER Type
        The type of content the playlist will contain. Valid values are:
        - video (default): Movies, TV shows, or other video content
        - audio: Music tracks
        - photo: Photos
 
    .PARAMETER RatingKey
        One or more media item rating keys to add to the playlist upon creation.
        Rating keys can be obtained from library browsing commands.
 
    .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 created playlist object.
 
    .EXAMPLE
        New-PatPlaylist -Title 'My Favorites'
 
        Creates a new empty video playlist named 'My Favorites'.
 
    .EXAMPLE
        New-PatPlaylist -Title 'Road Trip Music' -Type audio
 
        Creates a new empty audio playlist named 'Road Trip Music'.
 
    .EXAMPLE
        New-PatPlaylist -Title 'Weekend Watchlist' -RatingKey 12345, 67890 -PassThru
 
        Creates a playlist with two initial items and returns the created playlist object.
 
    .EXAMPLE
        Get-PatLibraryItem -SectionId 1 | Select-Object -First 5 -ExpandProperty ratingKey |
            ForEach-Object { New-PatPlaylist -Title 'Top 5' -RatingKey $_ }
 
        Creates a playlist from the first 5 items in library section 1.
 
    .OUTPUTS
        PlexAutomationToolkit.Playlist (when -PassThru is specified)
 
        Returns the created playlist object with properties:
        - PlaylistId: Unique playlist identifier
        - Title: Name of the playlist
        - Type: Playlist type (video, audio, photo)
        - ItemCount: Number of items in the playlist
        - ServerUri: The Plex server URI
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Title,

        [Parameter(Mandatory = $false)]
        [ValidateSet('video', 'audio', 'photo')]
        [string]
        $Type = 'video',

        [Parameter(Mandatory = $false, 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 (needed for adding items)
        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 {
            Write-Warning "Could not retrieve server machine identifier. Adding items during creation may fail."
        }

        # Collect all rating keys from pipeline
        $allRatingKeys = [System.Collections.ArrayList]::new()
    }

    process {
        # Collect rating keys from pipeline
        if ($RatingKey) {
            foreach ($key in $RatingKey) {
                $null = $allRatingKeys.Add($key)
            }
        }
    }

    end {
        if (-not $PSCmdlet.ShouldProcess($Title, 'Create playlist')) {
            return
        }

        try {
            # Build the creation URI with query parameters
            $queryParts = @(
                "type=$Type",
                "title=$([System.Uri]::EscapeDataString($Title))",
                'smart=0'
            )

            # Add items URI if we have rating keys and a machine identifier
            if ($allRatingKeys.Count -gt 0 -and $machineIdentifier) {
                # Build the library URI format Plex expects
                # 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 ','
                $queryParts += "uri=$([System.Uri]::EscapeDataString($uriParam))"
            }
            elseif ($allRatingKeys.Count -gt 0 -and -not $machineIdentifier) {
                Write-Warning "Cannot add items during creation: server machine identifier not available. Use Add-PatPlaylistItem after creation."
            }

            $queryString = $queryParts -join '&'
            $uri = Join-PatUri -BaseUri $effectiveUri -Endpoint '/playlists' -QueryString $queryString

            Write-Verbose "Creating playlist '$Title' of type '$Type'"

            $result = Invoke-PatApi -Uri $uri -Method 'POST' -Headers $headers -ErrorAction 'Stop'

            if ($PassThru -and $result) {
                # The API returns the created playlist in Metadata array
                $playlist = if ($result.Metadata) {
                    $result.Metadata | Select-Object -First 1
                }
                else {
                    $result
                }

                [PSCustomObject]@{
                    PSTypeName  = 'PlexAutomationToolkit.Playlist'
                    PlaylistId  = [int]$playlist.ratingKey
                    Title       = $playlist.title
                    Type        = $playlist.playlistType
                    ItemCount   = [int]$playlist.leafCount
                    Duration    = [long]$playlist.duration
                    Smart       = ($playlist.smart -eq '1' -or $playlist.smart -eq 1)
                    Composite   = $playlist.composite
                    AddedAt     = if ($playlist.addedAt) {
                        [DateTimeOffset]::FromUnixTimeSeconds([long]$playlist.addedAt).LocalDateTime
                    } else { $null }
                    UpdatedAt   = if ($playlist.updatedAt) {
                        [DateTimeOffset]::FromUnixTimeSeconds([long]$playlist.updatedAt).LocalDateTime
                    } else { $null }
                    ServerUri   = $effectiveUri
                }
            }
        }
        catch {
            throw "Failed to create playlist '$Title': $($_.Exception.Message)"
        }
    }
}