Public/Copy-PlexPlaylist.ps1
function Copy-PlexPlaylist { <# .SYNOPSIS This function will copy a playlist from your account to another user account on your server. Note: If the destination user already has a playlist with this name, a second one will be created. To overwrite, use the -Force switch. .DESCRIPTION This function will copy a playlist from your account to another user account on your server. Note: If the destination user already has a playlist with this name, a second one will be created. To overwrite, use the -Force switch. .PARAMETER Id Id of the playlist you wish to copy. To get this, use 'Get-PlexPlaylist'. .PARAMETER NewPlaylistName Create the playlist with a different name. .PARAMETER Username The username for the account you wish to copy the playlist to. .PARAMETER Force Overwrite the contents of an existing playlist. .EXAMPLE Copy-PlexPlaylist -Id 12345 -User 'user@domain.com' #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory = $true)] [String] $Id, [Parameter(Mandatory = $false)] [String] $NewPlaylistName, [Parameter(Mandatory = $true)] [String] $Username, [Parameter(Mandatory = $false)] [Switch] $Force ) ############################################################################# #Region Import Plex Configuration if(!$script:PlexConfigData) { try { Import-PlexConfiguration } catch { throw $_ } } #EndRegion ############################################################################# #Region Get the Playlist we want to copy Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Getting playlist $Id, including playlist items" try { # Get and filter: $Playlist = Get-PlexPlaylist -Id $Id -IncludeItems -ErrorAction Stop if(!$Playlist) { throw "Could not find playlist with id $Id." } } catch { throw $_ } #EndRegion ############################################################################# #Region Get the target user along with user token for our server Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Getting user." try { $User = Get-PlexUser -User $Username -IncludeToken -ErrorAction Stop if(($Null -eq $User) -or ($User.count -eq 0)) { throw "Could not get user: $Username" } } catch { throw $_ } #EndRegion ############################################################################# #Region Create a new variable to store the destination playlist name if($NewPlaylistName) { $PlaylistTitle = $NewPlaylistName } else { $PlaylistTitle = $Playlist.title } #EndRegion ############################################################################# #Region Check whether the user already has a playlist by this name. # It's worth noting that you can have multiple playlists with the same name (sigh). Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Checking $Username account for existing playlist" try { [Array]$ExistingPlaylistsWithSameName = Get-PlexPlaylist -AlternativeToken $User.token -ErrorAction Stop | Where-Object { $_.title -eq $PlaylistTitle } if($ExistingPlaylistsWithSameName.Count -gt 1) { # If there is more than 1 playlist with the same name in the destination account, we # 1) wouldn't know which we wanted to overwrite and # 2) wouldn't want to remove them automatically when -Force is used, so warn and exit. Write-Warning -Message "Multiple playlists with the name '$PlaylistTitle' exist under the destination account $Username. You can review these with 'Get-PlexPlaylist' and remove with 'Remove-PlexPlaylist' (after obtaining a user token for $Username with 'Get-PlexUser -Username $Username -IncludeToken')." return } elseif($ExistingPlaylistsWithSameName.Count -eq 1) { if($Force) { Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Playlist already exists in destination account. Removing." foreach($PL in $ExistingPlaylistsWithSameName) { try { #Invoke-RestMethod -Uri "$($DefaultPlexServer.Protocol)`://$($DefaultPlexServer.PlexServerHostname)`:$($DefaultPlexServer.Port)/playlists/$($PL.ratingKey)`?`X-Plex-Token=$($User.Token)" -Method DELETE | Out-Null Remove-PlexPlaylist -Id $PL.ratingKey -AlternativeToken $User.Token -ErrorAction Stop | Out-Null } catch { Write-Warning -Message "Could not delete existing playlist." throw $_ } } } else { Write-Warning -Message "The destination account already has a Playlist with the name '$PlaylistTitle'. To overwrite it, call this function with the -Force parameter." return } } else { } } catch { throw $_ } #EndRegion ############################################################################# #Region Get the machine Id property for the current plex server we're working with: Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Getting list of Plex servers" try { $CurrentPlexServer = Get-PlexServer -Name $DefaultPlexServer.PlexServer -ErrorAction Stop if(!$CurrentPlexServer) { throw "Could not find $CurrentPlexServer in $($Servers -join ', ')" } } catch { throw $_ } ############################################################################# # Establish whether the playlist is smart or not; this will determine how we create it: # If playlist is not smart: if($Playlist.smart -eq 0) { Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Original playlist is NOT smart." # Create a new playlist on the server, under the user's account: try { Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Creating playlist" $ItemsToAdd = $Playlist.Items.ratingKey -join ',' $Data = Invoke-RestMethod -Uri "$($DefaultPlexServer.Protocol)`://$($DefaultPlexServer.PlexServerHostname)`:$($DefaultPlexServer.Port)/playlists?uri=server://$($CurrentPlexServer.machineIdentifier)/com.plexapp.plugins.library/library/metadata/$ItemsToAdd&title=$PlaylistTitle&smart=0&type=$($PlayList.playlistType)&X-Plex-Token=$($User.Token)" -Method POST return $Data.MediaContainer.Playlist } catch { throw $_ } } elseif($Playlist.smart -eq 1) { Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Original playlist is smart." # Although we have the playlist object from Get-PlexPlaylist, this function makes a query for all playlists before returning based on a match # by the playlist name. With this, we're not given a property called .content which contains the data that defines *how* the playlist is smart. # So, make an additional lookup to get the playlist explicitly by Id, and include the items this time: $PlaylistData = Get-PlexPlaylist -Id $Playlist.ratingKey -IncludeItems -ErrorAction Stop | Where-Object { $_.title -eq $PlaylistName } # Parse the data in the playlist to establish what parameters were used to create the smart playlist. # Split on the 'all?': $SmartPlaylistParams = ($PlaylistData.content -split 'all%3F')[1] try { Write-Verbose -Message "Function: $($MyInvocation.MyCommand): Creating playlist" $Data = Invoke-RestMethod -Uri "$($DefaultPlexServer.Protocol)`://$($DefaultPlexServer.PlexServerHostname)`:$($DefaultPlexServer.Port)/playlists?uri=server://$($CurrentPlexServer.machineIdentifier)/com.plexapp.plugins.library/library/sections/2/all?$SmartPlaylistParams&title=$PlaylistTitle&smart=1&type=video&X-Plex-Product=Plex%20Web&X-Plex-Version=3.95.2&X-Plex-Client-Identifier=ni91ijrs5miuwc37d5esdrr3&X-Plex-Platform=Chrome&X-Plex-Platform-Version=75.0&X-Plex-Sync-Version=2&X-Plex-Model=bundled&X-Plex-Device=Windows&X-Plex-Device-Name=Chrome&X-Plex-Device-Screen-Resolution=1088x937%2C1920x1080&X-Plex-Token=$($User.Token)&X-Plex-Language=en&X-Plex-Text-Format=plain" -Method POST return $Data.MediaContainer.Playlist } catch { throw $_ } } else { Write-Warning -Message "Function: $($MyInvocation.MyCommand): No work done." } } |