Private/Remove-PatWatchedPlaylistItem.ps1

function Remove-PatWatchedPlaylistItem {
    <#
    .SYNOPSIS
        Removes watched items from a Plex playlist based on watch status differences.

    .DESCRIPTION
        Internal helper function that removes items from a playlist that have been
        watched on another server. Takes watch status differences (from Compare-PatWatchStatus)
        and removes matching items from the specified playlist. Shows progress during
        removal and handles errors gracefully.

    .PARAMETER WatchDiff
        Array of watch status differences from Compare-PatWatchStatus. Each object
        should have a TargetRatingKey property to match against playlist items.

    .PARAMETER PlaylistId
        The unique identifier of the playlist to remove items from.

    .PARAMETER PlaylistName
        The name of the playlist to remove items from. Used when PlaylistId is not specified.

    .PARAMETER ServerUri
        The base URI of the Plex server.

    .PARAMETER Token
        The Plex authentication token for API requests.

    .PARAMETER ServerName
        The name of a stored server to use. Alternative to ServerUri/Token.

    .OUTPUTS
        System.Int32
        Returns the count of items successfully removed from the playlist.

    .EXAMPLE
        $diffs = Compare-PatWatchStatus -SourceServerName 'Travel' -TargetServerName 'Home' -WatchedOnSourceOnly
        Remove-PatWatchedPlaylistItem -WatchDiff $diffs -PlaylistName 'Travel' -ServerName 'Home'

        Removes watched items from the Travel playlist.

    .EXAMPLE
        $removed = Remove-PatWatchedPlaylistItem -WatchDiff $diffs -PlaylistId 100 -ServerUri 'http://plex:32400' -Token $token
        Write-Host "Removed $removed items"

        Removes watched items and returns the count.
    #>

    [CmdletBinding()]
    [OutputType([int])]
    param (
        [Parameter(Mandatory = $true)]
        [AllowEmptyCollection()]
        [PSObject[]]
        $WatchDiff,

        [Parameter(Mandatory = $false)]
        [int]
        $PlaylistId,

        [Parameter(Mandatory = $false)]
        [string]
        $PlaylistName,

        [Parameter(Mandatory = $false)]
        [string]
        $ServerUri,

        [Parameter(Mandatory = $false)]
        [string]
        $Token,

        [Parameter(Mandatory = $false)]
        [string]
        $ServerName
    )

    process {
        # Return early if no watch differences
        if (-not $WatchDiff -or $WatchDiff.Count -eq 0) {
            Write-Verbose "No watch differences provided"
            return 0
        }

        # Build parameters for Get-PatPlaylist
        $getPlaylistParameters = @{
            IncludeItems = $true
            ErrorAction  = 'Stop'
        }

        if ($PlaylistId) {
            $getPlaylistParameters['PlaylistId'] = $PlaylistId
        }
        elseif ($PlaylistName) {
            $getPlaylistParameters['PlaylistName'] = $PlaylistName
        }
        else {
            Write-Warning "Either PlaylistId or PlaylistName must be specified"
            return 0
        }

        if ($ServerName) {
            $getPlaylistParameters['ServerName'] = $ServerName
        }
        elseif ($ServerUri) {
            $getPlaylistParameters['ServerUri'] = $ServerUri
            if ($Token) {
                $getPlaylistParameters['Token'] = $Token
            }
        }

        # Get playlist with items
        try {
            $playlist = Get-PatPlaylist @getPlaylistParameters
        }
        catch {
            Write-Warning "Failed to get playlist: $($_.Exception.Message)"
            return 0
        }

        if (-not $playlist.Items -or $playlist.Items.Count -eq 0) {
            Write-Verbose "Playlist '$($playlist.Title)' has no items"
            return 0
        }

        # Build lookup from RatingKey to PlaylistItem
        $ratingKeyToItem = @{}
        foreach ($item in $playlist.Items) {
            $ratingKeyToItem[[string]$item.RatingKey] = $item
        }

        # Find watched items that are in the playlist
        # Use TargetRatingKey which corresponds to the source (home) server's keys
        $itemsToRemove = [System.Collections.Generic.List[hashtable]]::new()
        foreach ($diff in $WatchDiff) {
            $key = [string]$diff.TargetRatingKey
            if ($ratingKeyToItem.ContainsKey($key)) {
                $itemsToRemove.Add(@{
                    PlaylistItem = $ratingKeyToItem[$key]
                    WatchDiff    = $diff
                })
            }
        }

        if ($itemsToRemove.Count -eq 0) {
            Write-Verbose "No watched items found in playlist to remove"
            return 0
        }

        Write-Verbose "Found $($itemsToRemove.Count) watched items in playlist '$($playlist.Title)'"

        # Remove items from playlist
        $removedCount = 0
        $totalToRemove = $itemsToRemove.Count
        $currentItem = 0

        foreach ($itemToRemove in $itemsToRemove) {
            $currentItem++
            $playlistItem = $itemToRemove.PlaylistItem
            $itemDisplay = Format-PatMediaItemName -Item $playlistItem

            $percentComplete = [int](($currentItem / $totalToRemove) * 100)
            Write-Progress -Activity "Removing watched items from playlist" `
                -Status "Removing $currentItem of $totalToRemove`: $itemDisplay" `
                -PercentComplete $percentComplete `
                -Id 1

            try {
                $removeParams = @{
                    PlaylistId     = $playlist.PlaylistId
                    PlaylistItemId = $playlistItem.PlaylistItemId
                    Confirm        = $false
                    ErrorAction    = 'Stop'
                }
                if ($ServerName) {
                    $removeParams['ServerName'] = $ServerName
                }
                elseif ($ServerUri) {
                    $removeParams['ServerUri'] = $ServerUri
                    if ($Token) {
                        $removeParams['Token'] = $Token
                    }
                }
                Remove-PatPlaylistItem @removeParams

                $removedCount++
                Write-Verbose "Removed '$itemDisplay' from playlist"
            }
            catch {
                Write-Warning "Failed to remove '$itemDisplay': $($_.Exception.Message)"
            }
        }

        Write-Progress -Activity "Removing watched items from playlist" -Completed -Id 1
        Write-Verbose "Removed $removedCount watched items from playlist"

        return $removedCount
    }
}