Public/Get-PatLibraryChildItem.ps1
|
function Get-PatLibraryChildItem { <# .SYNOPSIS Lists directories and files at a given path on the Plex server. .DESCRIPTION Browses the filesystem on the Plex server, listing subdirectories and files at a specified path. Uses the Plex internal browse service endpoint. .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 Path The absolute filesystem path to browse (e.g., /mnt/media, /var/lib/plexmediaserver) If omitted, lists root-level accessible paths. .PARAMETER SectionId Optional library section ID. When provided, the command browses each path configured for that section. Cannot be combined with SectionName. .PARAMETER SectionName Optional library section name (e.g., "Movies"). When provided, the command browses each path configured for that section. Cannot be combined with SectionId. .EXAMPLE Get-PatLibraryChildItem -ServerUri "http://plex.example.com:32400" -Path "/mnt/media" Lists directories and files under /mnt/media. .EXAMPLE Get-PatLibraryChildItem Lists root-level paths from the default stored server. .EXAMPLE Get-PatLibraryChildItem -Path "/mnt/smb/nas5/movies" Lists all items (directories and files) under the movies path. .EXAMPLE Get-PatLibraryChildItem -SectionName "Movies" Lists items from every path configured for the Movies section. .OUTPUTS PSCustomObject #> [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(DefaultParameterSetName = 'PathOnly')] param ( [Parameter(Mandatory = $false, ParameterSetName = 'PathOnly')] [Parameter(Mandatory = $false, ParameterSetName = 'ByName')] [Parameter(Mandatory = $false, ParameterSetName = 'ById')] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter(Mandatory = $false, ParameterSetName = 'ByName')] [ValidateNotNullOrEmpty()] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # Strip leading quotes for matching (case-insensitive) $quoteChar = '' $strippedWord = $wordToComplete if ($wordToComplete -match "^([`"'])(.*)$") { $quoteChar = $Matches[1] $strippedWord = $Matches[2] } # Use provided ServerUri if available, otherwise use default server if ($fakeBoundParameters.ContainsKey('ServerUri')) { try { $sections = Get-PatLibrary -ServerUri $fakeBoundParameters['ServerUri'] -ErrorAction 'SilentlyContinue' foreach ($sectionTitle in $sections.Directory.title) { if ($sectionTitle -ilike "$strippedWord*") { if ($quoteChar) { $completionText = "$quoteChar$sectionTitle$quoteChar" } elseif ($sectionTitle -match '\s') { $completionText = "'$sectionTitle'" } else { $completionText = $sectionTitle } [System.Management.Automation.CompletionResult]::new($completionText, $sectionTitle, 'ParameterValue', $sectionTitle) } } } catch { Write-Debug "Tab completion failed for SectionName: $($_.Exception.Message)" } } else { # Fall back to default server - don't pass ServerUri so Get-PatLibrary retrieves server object with token try { $sections = Get-PatLibrary -ErrorAction 'SilentlyContinue' foreach ($sectionTitle in $sections.Directory.title) { if ($sectionTitle -ilike "$strippedWord*") { if ($quoteChar) { $completionText = "$quoteChar$sectionTitle$quoteChar" } elseif ($sectionTitle -match '\s') { $completionText = "'$sectionTitle'" } else { $completionText = $sectionTitle } [System.Management.Automation.CompletionResult]::new($completionText, $sectionTitle, 'ParameterValue', $sectionTitle) } } } catch { Write-Debug "Tab completion failed for SectionName (default server): $($_.Exception.Message)" } } })] [string] $SectionName, [Parameter(Mandatory = $false, ParameterSetName = 'ById')] [ValidateRange(1, [int]::MaxValue)] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # Strip leading quotes for matching $strippedWord = $wordToComplete -replace "^[`"']", '' # Use provided ServerUri if available, otherwise use default server if ($fakeBoundParameters.ContainsKey('ServerUri')) { try { $sections = Get-PatLibrary -ServerUri $fakeBoundParameters['ServerUri'] -ErrorAction 'SilentlyContinue' $sections.Directory | ForEach-Object { $sectionId = ($_.key -replace '.*/(\d+)$', '$1') if ($sectionId -ilike "$strippedWord*") { [System.Management.Automation.CompletionResult]::new($sectionId, "$sectionId - $($_.title)", 'ParameterValue', "$($_.title) (ID: $sectionId)") } } } catch { Write-Debug "Tab completion failed for SectionId: $($_.Exception.Message)" } } else { # Fall back to default server - don't pass ServerUri so Get-PatLibrary retrieves server object with token try { $sections = Get-PatLibrary -ErrorAction 'SilentlyContinue' $sections.Directory | ForEach-Object { $sectionId = ($_.key -replace '.*/(\d+)$', '$1') if ($sectionId -ilike "$strippedWord*") { [System.Management.Automation.CompletionResult]::new($sectionId, "$sectionId - $($_.title)", 'ParameterValue', "$($_.title) (ID: $sectionId)") } } } catch { Write-Debug "Tab completion failed for SectionId (default server): $($_.Exception.Message)" } } })] [int] $SectionId, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-PatServerUri -Uri $_ })] [string] $ServerUri ) # Use default server if ServerUri not specified $server = $null $effectiveUri = $ServerUri $usingDefaultServer = $false 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 $usingDefaultServer = $true } catch { throw "Failed to get default server: $($_.Exception.Message)" } } try { $pathsToBrowse = @() # If section parameters provided, collect all section locations if ($SectionName -or $SectionId) { # If using default server, don't pass ServerUri so Get-PatLibrary can retrieve server object with token if ($usingDefaultServer) { $sections = Get-PatLibrary -ErrorAction 'Stop' } else { $sections = Get-PatLibrary -ServerUri $effectiveUri -ErrorAction 'Stop' } $matchingSection = $null if ($SectionName) { $matchingSection = $sections.Directory | Where-Object { $_.title -eq $SectionName } if (-not $matchingSection) { throw "Library section '$SectionName' not found" } } else { $matchingSection = $sections.Directory | Where-Object { ($_.key -replace '.*/(\d+)$', '$1') -eq $SectionId.ToString() } if (-not $matchingSection) { throw "Library section with ID $SectionId not found" } } if ($matchingSection.Location) { # Handle both single location and array of locations $locations = @($matchingSection.Location) foreach ($location in $locations) { if ($location.path) { $pathsToBrowse += $location.path } } } } if ($Path) { # Explicit path overrides any section-derived paths $pathsToBrowse = @($Path) } if (-not $pathsToBrowse -or $pathsToBrowse.Count -eq 0) { # No path specified, no section provided -> browse root $pathsToBrowse = @($null) } # Build headers with authentication if we have server object $headers = if ($server) { Get-PatAuthHeaders -Server $server } else { @{ Accept = 'application/json' } } $results = @() foreach ($p in $pathsToBrowse) { $endpoint = '/services/browse' if ($p) { $pathBytes = [System.Text.Encoding]::UTF8.GetBytes($p) $pathB64 = [Convert]::ToBase64String($pathBytes) $endpoint += "/$pathB64" } $uri = Join-PatUri -BaseUri $effectiveUri -Endpoint $endpoint -QueryString 'includeFiles=1' $result = Invoke-PatApi -Uri $uri -Headers $headers -ErrorAction 'Stop' if ($result.Path) { $results += $result.Path } if ($result.File) { $results += $result.File } if (-not $result.Path -and -not $result.File) { Write-Verbose "No items found at path: $p" } } $results } catch { $errPath = if ($Path) { $Path } elseif ($SectionName) { $SectionName } elseif ($SectionId) { $SectionId } else { '<root>' } throw "Failed to list items for ${errPath}: $($_.Exception.Message)" } } |